Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions Microsoft.OpenApi.sln
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E546B92F-20A
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2DE4-4900-ADB9-ABC37052040A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -49,6 +51,10 @@ Global
{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.Build.0 = Release|Any CPU
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -59,6 +65,7 @@ Global
{79933258-0126-4382-8755-D50820ECC483} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
{AD83F991-DBF3-4251-8613-9CC54C826964} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F171EFC-0DB5-4B10-ABFA-AF48D52CC565}
Expand Down
41 changes: 28 additions & 13 deletions src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Interface;
Expand Down Expand Up @@ -58,21 +59,35 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
ExtensionParsers = _settings.ExtensionParsers
};

// Parse the OpenAPI Document
var document = context.Parse(yamlDocument, diagnostic);
OpenApiDocument document = null;

// Resolve References if requested
switch (_settings.ReferenceResolution)
try
{
// Parse the OpenAPI Document
document = context.Parse(yamlDocument, diagnostic);

// Resolve References if requested
switch (_settings.ReferenceResolution)
{
case ReferenceResolutionSetting.ResolveAllReferences:
Copy link
Contributor

@xuzhg xuzhg Mar 9, 2018

Choose a reason for hiding this comment

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

maybe to use the "default: throw ..." is better. #WontFix

Copy link
Member Author

@darrelmiller darrelmiller Mar 9, 2018

Choose a reason for hiding this comment

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

This chunk of code is going to change fairly significantly when adding support for resolving external files, so I'll review this in a future PR. #Closed

throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously);
case ReferenceResolutionSetting.ResolveLocalReferences:
var resolver = new OpenApiReferenceResolver(document);
var walker = new OpenApiWalker(resolver);
walker.Walk(document);
foreach (var item in resolver.Errors)
{
diagnostic.Errors.Add(item);
}
break;
case ReferenceResolutionSetting.DoNotResolveReferences:
break;
}

}
catch (OpenApiException ex)
{
case ReferenceResolutionSetting.ResolveAllReferences:
throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously);
case ReferenceResolutionSetting.ResolveLocalReferences:
var resolver = new OpenApiReferenceResolver(document);
var walker = new OpenApiWalker(resolver);
walker.Walk(document);
break;
case ReferenceResolutionSetting.DoNotResolveReferences:
break;
diagnostic.Errors.Add(new OpenApiError(ex));
}

// Validate the document
Expand Down
17 changes: 11 additions & 6 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,33 @@ public ListNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlSequen

public override List<T> CreateList<T>(Func<MapNode, T> map)
{
var yamlSequence = _nodeList;
if (yamlSequence == null)
if (_nodeList == null)
{
throw new OpenApiException(
$"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}");
}

return yamlSequence.Select(n => map(new MapNode(Context, Diagnostic, n as YamlMappingNode)))
return _nodeList.Select(n => map(new MapNode(Context, Diagnostic, n as YamlMappingNode)))
.Where(i => i != null)
.ToList();
}

public override List<IOpenApiAny> CreateListOfAny()
{
return _nodeList.Select(n => ParseNode.Create(Context, Diagnostic,n).CreateAny())
.Where(i => i != null)
.ToList();
}

public override List<T> CreateSimpleList<T>(Func<ValueNode, T> map)
{
var yamlSequence = _nodeList;
if (yamlSequence == null)
if (_nodeList == null)
{
throw new OpenApiException(
$"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}");
}

return yamlSequence.Select(n => map(new ValueNode(Context, Diagnostic, (YamlScalarNode)n))).ToList();
return _nodeList.Select(n => map(new ValueNode(Context, Diagnostic, (YamlScalarNode)n))).ToList();
}

public IEnumerator<ParseNode> GetEnumerator()
Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ public override Dictionary<string, T> CreateMapWithReference<T>(
key = n.Key.GetScalarValue(),
value = map(new MapNode(Context, Diagnostic, (YamlMappingNode)n.Value))
};
if (entry.value == null)
{
return null; // Body Parameters shouldn't be converted to Parameters
}
entry.value.Reference = new OpenApiReference()
{
Type = referenceType,
Expand All @@ -103,7 +107,7 @@ public override Dictionary<string, T> CreateMapWithReference<T>(
return entry;
}
);
return nodes.ToDictionary(k => k.key, v => v.value);
return nodes.Where(n => n!= null).ToDictionary(k => k.key, v => v.value);
}

public override Dictionary<string, T> CreateSimpleMap<T>(Func<ValueNode, T> map)
Expand Down
29 changes: 15 additions & 14 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public MapNode CheckMapNode(string nodeName)
var mapNode = this as MapNode;
if (mapNode == null)
{
Diagnostic.Errors.Add(
new OpenApiError("", $"{nodeName} must be a map/object at " + Context.GetLocation()));
throw new OpenApiException($"{nodeName} must be a map/object");
Copy link
Contributor

@xuzhg xuzhg Mar 9, 2018

Choose a reason for hiding this comment

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

why change to throw exception? #Resolved

Copy link
Member Author

@darrelmiller darrelmiller Mar 9, 2018

Choose a reason for hiding this comment

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

Because it caused a cascade of errors as following code attempted to handle the null node. #Resolved

}

return mapNode;
Expand Down Expand Up @@ -69,49 +68,51 @@ public static ParseNode Create(ParsingContext context, OpenApiDiagnostic diagnos

public virtual List<T> CreateList<T>(Func<MapNode, T> map)
{
throw new OpenApiException("Cannot create list");
throw new OpenApiException("Cannot create list from this type of node.");
}

public virtual Dictionary<string, T> CreateMap<T>(Func<MapNode, T> map)
{
throw new OpenApiException("Cannot create map");
throw new OpenApiException("Cannot create map from this type of node.");
}

public virtual Dictionary<string, T> CreateMapWithReference<T>(
ReferenceType referenceType,
Func<MapNode, T> map)
where T : class, IOpenApiReferenceable
{
throw new OpenApiException("Cannot create map from reference");
throw new OpenApiException("Cannot create map from this reference.");
}

public virtual List<T> CreateSimpleList<T>(Func<ValueNode, T> map)
{
throw new OpenApiException("Cannot create simple list");
throw new OpenApiException("Cannot create simple list from this type of node.");
}

public virtual Dictionary<string, T> CreateSimpleMap<T>(Func<ValueNode, T> map)
{
throw new OpenApiException("Cannot create simple map");
throw new OpenApiException("Cannot create simple map from this type of node.");
}

/// <summary>
/// Create a <see cref="IOpenApiAny"/>
/// </summary>
/// <returns></returns>
public virtual IOpenApiAny CreateAny()
{
throw new NotSupportedException();
throw new OpenApiException("Cannot create an Any object this type of node.");
}

public virtual string GetRaw()
{
throw new OpenApiException("Cannot get raw value");
throw new OpenApiException("Cannot get raw value from this type of node.");
}

public virtual string GetScalarValue()
{
throw new OpenApiException("Cannot get scalar value");
throw new OpenApiException("Cannot create a scalar value from this type of node.");
}

public virtual List<IOpenApiAny> CreateListOfAny()
{
throw new OpenApiException("Cannot create a list from this type of node.");
}

}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public override IOpenApiAny CreateAny()
dblValue); // Note(darrmi): This may be better as decimal. Further investigation required.
}

if (DateTime.TryParse(value, out var datetimeValue))
if (DateTimeOffset.TryParse(value, out var datetimeValue))
{
return new OpenApiDateTime(datetimeValue);
}
Expand Down
22 changes: 20 additions & 2 deletions src/Microsoft.OpenApi.Readers/Services/OpenApiReferenceResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
Expand All @@ -17,13 +18,22 @@ internal class OpenApiReferenceResolver : OpenApiVisitorBase
{
private OpenApiDocument _currentDocument;
private bool _resolveRemoteReferences;
private List<OpenApiError> _errors = new List<OpenApiError>();

public OpenApiReferenceResolver(OpenApiDocument currentDocument, bool resolveRemoteReferences = true)
{
_currentDocument = currentDocument;
_resolveRemoteReferences = resolveRemoteReferences;
}

public IEnumerable<OpenApiError> Errors
{
get
{
return _errors;
}
}

public override void Visit(OpenApiDocument doc)
{
if (doc.Tags != null)
Expand Down Expand Up @@ -202,11 +212,19 @@ private void ResolveTags(IList<OpenApiTag> tags)
{
if (string.IsNullOrEmpty(reference.ExternalResource))
{
return _currentDocument.ResolveReference(reference) as T;
try
{
return _currentDocument.ResolveReference(reference) as T;
}
catch (OpenApiException ex)
{
_errors.Add(new OpenApiError(ex));
return null;
}
}
else if (_resolveRemoteReferences == true)
{
// TODO: Resolve Remote reference
// TODO: Resolve Remote reference (Targeted for 1.1 release)
return new T()
{
UnresolvedReference = true,
Expand Down
56 changes: 56 additions & 0 deletions src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Readers.V2
{
Expand Down Expand Up @@ -65,6 +68,17 @@ internal static partial class OpenApiV2Deserializer
o.Components.Parameters = n.CreateMapWithReference(
ReferenceType.Parameter,
LoadParameter);

o.Components.RequestBodies = n.CreateMapWithReference(ReferenceType.RequestBody, p =>
{
var parameter = LoadParameter(p, evenBody: true);
if (parameter.In == null)
{
return CreateRequestBody(n.Context,parameter);
}
return null;
}
);
}
},
{
Expand Down Expand Up @@ -139,7 +153,49 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)

MakeServers(openApidoc.Servers, openApiNode.Context);

FixRequestBodyReferences(openApidoc);
return openApidoc;
}

private static void FixRequestBodyReferences(OpenApiDocument doc)
{
// Walk all unresolved parameter references
// if id matches with request body Id, change type
if (doc.Components?.RequestBodies != null && doc.Components?.RequestBodies.Count > 0)
{
var fixer = new RequestBodyReferenceFixer(doc.Components?.RequestBodies);
var walker = new OpenApiWalker(fixer);
walker.Walk(doc);
}

}
}

internal class RequestBodyReferenceFixer : OpenApiVisitorBase
{
private IDictionary<string, OpenApiRequestBody> _requestBodies;
public RequestBodyReferenceFixer(IDictionary<string, OpenApiRequestBody> requestBodies)
{
_requestBodies = requestBodies;
}
public override void Visit(OpenApiOperation operation)
{
var body = operation.Parameters.Where(p => p.UnresolvedReference == true
&& _requestBodies.ContainsKey(p.Reference.Id)).FirstOrDefault();

if (body != null)
{
operation.Parameters.Remove(body);
operation.RequestBody = new OpenApiRequestBody()
{
UnresolvedReference = true,
Reference = new OpenApiReference()
{
Id = body.Reference.Id,
Type = ReferenceType.RequestBody
}
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private static OpenApiRequestBody CreateFormBody(ParsingContext context, List<Op
return formBody;
}

private static OpenApiRequestBody CreateRequestBody(
internal static OpenApiRequestBody CreateRequestBody(
ParsingContext context,
OpenApiParameter bodyParameter)
{
Expand Down
Loading