Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,35 @@

namespace Microsoft.OpenApi.Readers
{
/// <summary>
/// Indicates if and when the reader should convert unresolved references into resolved objects
/// </summary>
public enum ReferenceResolutionSetting
{
/// <summary>
/// Create placeholder objects with an OpenApiReference instance and UnresolvedReference set to true.
/// </summary>
DoNotResolveReferences,
/// <summary>
/// Convert local references to references of valid domain objects.
/// </summary>
ResolveLocalReferences,
/// <summary>
/// Convert all references to references of valid domain objects.
/// </summary>
ResolveRemoteReferences
Copy link
Contributor

@PerthCharern PerthCharern Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResolveRemoteReferences [](start = 8, length = 23)

either ResolveAllReferences or ResolveLocalAndRemoteReferences #Closed

}

/// <summary>
/// Configuration settings to control how OpenAPI documents are parsed
/// </summary>
public class OpenApiReaderSettings
{
/// <summary>
/// Indicates how references in the source document should be handled.
/// </summary>
public ReferenceResolutionSetting ReferenceResolution { get; set; } = ReferenceResolutionSetting.ResolveLocalReferences;

/// <summary>
/// Dictionary of parsers for converting extensions into strongly typed classes
/// </summary>
Expand Down
18 changes: 17 additions & 1 deletion src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.IO;
using System.Linq;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Interface;
using Microsoft.OpenApi.Readers.Services;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations;
using SharpYaml;
using SharpYaml.Serialization;

Expand Down Expand Up @@ -60,6 +61,21 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
// Parse the OpenAPI Document
var document = context.Parse(yamlDocument, diagnostic);

// Resolve References if requested
switch (_settings.ReferenceResolution)
{
case ReferenceResolutionSetting.ResolveRemoteReferences:
throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously); [](start = 20, length = 94)

This will be supported, right? Can we just go with NotImplementedException()?

Copy link
Member Author

@darrelmiller darrelmiller Feb 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will be handled. If you want to resolve remote references then you should call the ReadAsync method instead. The ReadAsync will also load related documents into a workspace. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.


In reply to: 167757446 [](ancestors = 167757446)

case ReferenceResolutionSetting.ResolveLocalReferences:
var resolver = new OpenApiReferenceResolver(document);
var walker = new OpenApiWalker(resolver);
walker.Walk(document);
break;
case
ReferenceResolutionSetting.DoNotResolveReferences:
Copy link
Contributor

@PerthCharern PerthCharern Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, remove this awkward linebreak :)

break;
}

// Validate the document
var errors = document.Validate(_settings.RuleSet);
foreach (var item in errors)
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using SharpYaml.Serialization;

namespace Microsoft.OpenApi.Readers.ParseNodes
Expand Down
58 changes: 33 additions & 25 deletions src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,33 @@ public override Dictionary<string, T> CreateMap<T>(Func<MapNode, T> map)
return nodes.ToDictionary(k => k.key, v => v.value);
}

public override Dictionary<string, T> CreateMapWithReference<T>(
ReferenceType referenceType,
string refpointerbase,
Func<MapNode, T> map)
{
var yamlMap = _node;
if (yamlMap == null)
{
throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
}

var nodes = yamlMap.Select(
n => new
{
key = n.Key.GetScalarValue(),
value = GetReferencedObject<T>(referenceType, refpointerbase + n.Key.GetScalarValue()) ??
map(new MapNode(Context, Diagnostic, (YamlMappingNode)n.Value))
});
return nodes.ToDictionary(k => k.key, v => v.value);
}
public override Dictionary<string, T> CreateMapWithReference<T>(
ReferenceType referenceType,
Func<MapNode, T> map)
{
var yamlMap = _node;
if (yamlMap == null)
{
throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
}

var nodes = yamlMap.Select(
n => {
var entry = new
{
key = n.Key.GetScalarValue(),
value = map(new MapNode(Context, Diagnostic, (YamlMappingNode)n.Value))
};
entry.value.Reference = new OpenApiReference()
{
Type = referenceType,
Id = entry.key
};
return entry;
}
);
return nodes.ToDictionary(k => k.key, v => v.value);
}

public override Dictionary<string, T> CreateSimpleMap<T>(Func<ValueNode, T> map)
{
Expand Down Expand Up @@ -133,12 +140,13 @@ public override string GetRaw()
}

public T GetReferencedObject<T>(ReferenceType referenceType, string referenceId)
where T : IOpenApiReferenceable
where T : IOpenApiReferenceable, new()
{
return (T)Context.GetReferencedObject(
Diagnostic,
referenceType,
referenceId);
return new T()
{
UnresolvedReference = true,
Reference = Context.VersionService.ConvertToOpenApiReference(referenceId,referenceType)
};
}

public string GetReferencePointer()
Expand Down
1 change: 0 additions & 1 deletion src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ public virtual Dictionary<string, T> CreateMap<T>(Func<MapNode, T> map)

public virtual Dictionary<string, T> CreateMapWithReference<T>(
ReferenceType referenceType,
string refpointer,
Func<MapNode, T> map)
where T : class, IOpenApiReferenceable
{
Expand Down
94 changes: 48 additions & 46 deletions src/Microsoft.OpenApi.Readers/ParsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ namespace Microsoft.OpenApi.Readers
public class ParsingContext
{
private readonly Stack<string> _currentLocation = new Stack<string>();
private readonly Dictionary<string, IOpenApiReferenceable> _referenceStore = new Dictionary<string, IOpenApiReferenceable>();
private readonly Dictionary<string, object> _tempStorage = new Dictionary<string, object>();
private IOpenApiVersionService _versionService;

private readonly Dictionary<string, Stack<string>> _loopStacks = new Dictionary<string, Stack<string>>();
internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();

internal RootNode RootNode { get; set; }
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();


/// <summary>
/// Initiates the parsing process. Not thread safe and should only be called once on a parsing context
/// </summary>
Expand Down Expand Up @@ -131,48 +128,6 @@ public string GetLocation()
return "#/" + string.Join("/", _currentLocation.Reverse().ToArray());
}

/// <summary>
/// Gets the referenced object.
/// </summary>
public IOpenApiReferenceable GetReferencedObject(
OpenApiDiagnostic diagnostic,
ReferenceType referenceType,
string referenceString)
{
_referenceStore.TryGetValue(referenceString, out var referencedObject);

// If reference has already been accessed once, simply return the same reference object.
if (referencedObject != null)
{
return referencedObject;
}

var reference = VersionService.ConvertToOpenApiReference(referenceString, referenceType);

var isReferencedObjectFound = VersionService.TryLoadReference(this, reference, out referencedObject);

if (isReferencedObjectFound)
{
// Populate the Reference section of the object, so that the writers
// can recognize that this is referencing another object.
referencedObject.Reference = reference;
_referenceStore.Add(referenceString, referencedObject);
}
else if (referencedObject != null)
{
return referencedObject;
}
else
{
diagnostic.Errors.Add(
new OpenApiError(
GetLocation(),
$"Cannot resolve the reference {referenceString}"));
}

return referencedObject;
}

/// <summary>
/// Gets the value from the temporary storage matching the given key.
/// </summary>
Expand Down Expand Up @@ -201,5 +156,52 @@ public void StartObject(string objectName)
{
_currentLocation.Push(objectName);
}

/// <summary>
/// Maintain history of traversals to avoid stack overflows from cycles
/// </summary>
/// <param name="loopId">Any unique identifier for a stack</param>
/// <param name="key">Identifier used to </param>
/// <returns></returns>
Copy link
Contributor

@PerthCharern PerthCharern Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// [](start = 7, length = 24)

nit, you can just remove these non-filled tags #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, for this one, you might want to fill since it's not that obvious what the returned bool represents.


In reply to: 167710868 [](ancestors = 167710868)

public bool PushLoop(string loopId, string key)
{
Stack<string> stack;
if (!_loopStacks.TryGetValue(loopId, out stack))
{
stack = new Stack<string>();
_loopStacks.Add(loopId, stack);
}

if (!stack.Contains(key))
{
stack.Push(key);
return true;
} else
{
return false; // Loop detected
}
}

/// <summary>
/// Reset loop tracking stack
/// </summary>
/// <param name="loopid"></param>
internal void ClearLoop(string loopid)
Copy link
Contributor

@PerthCharern PerthCharern Feb 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loopid [](start = 39, length = 6)

nit, loopId #Closed

{
_loopStacks[loopid].Clear();
}

/// <summary>
/// Exit from the context in cycle detection
/// </summary>
/// <param name="loopid"></param>
public void PopLoop(string loopid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loopid [](start = 35, length = 6)

nit, loopId

{
if (_loopStacks[loopid].Count > 0)
{
_loopStacks[loopid].Pop();
}
}

}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Microsoft.OpenApi.Readers/Properties/SRResource.resx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@
<data name="ArgumentNullOrWhiteSpace" xml:space="preserve">
<value>The argument '{0}' is null, empty or consists only of white-space.</value>
</data>
<data name="CannotResolveRemoteReferencesSynchronously" xml:space="preserve">
<value>"Cannot resolve remote references automatically in a syncronous call."</value>
</data>
<data name="LoadReferencedObjectFromExternalNotImplmented" xml:space="preserve">
<value>Not implemented to find referenced element from external resource.</value>
</data>
Expand Down
Loading