Skip to content

Commit 59564e0

Browse files
authored
Merge branch 'vnext' into feature/add_validator_for_path_parameters_not_present_in_path
2 parents 481d3bc + 55317d3 commit 59564e0

File tree

102 files changed

+14948
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+14948
-101
lines changed

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"workbench.colorCustomizations": {
3+
"activityBar.background": "#03323C",
4+
"titleBar.activeBackground": "#054754",
5+
"titleBar.activeForeground": "#F0FCFE"
6+
}
7+
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
The **OpenAPI.NET** SDK contains a useful object model for OpenAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.
1212

13-
**See more information on the OpenAPI specification and its history here: <a href="https://www.openapis.org">Open API Initiative</a>**
13+
**See more information on the OpenAPI specification and its history here: <a href="https://www.openapis.org">OpenAPI Initiative</a>**
1414

1515
Project Objectives
1616

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
using Microsoft.OpenApi.Models;
8+
9+
namespace Microsoft.OpenApi.Readers.Interface
10+
{
11+
/// <summary>
12+
/// Interface for service that translates a URI into a stream that can be loaded by a Reader
13+
/// </summary>
14+
public interface IStreamLoader
15+
{
16+
/// <summary>
17+
/// Use Uri to locate data and convert into an input object.
18+
/// </summary>
19+
/// <param name="uri">Identifier of some source of an OpenAPI Description</param>
20+
/// <returns>A data objext that can be processed by a reader to generate an <see cref="OpenApiDocument"/></returns>
21+
Task<Stream> LoadAsync(Uri uri);
22+
23+
/// <summary>
24+
/// Use Uri to locate data and convert into an input object.
25+
/// </summary>
26+
/// <param name="uri"></param>
27+
/// <returns></returns>
28+
Stream Load(Uri uri);
29+
}
30+
}

src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net46; netstandard2.0</TargetFrameworks>
3+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
44
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
55
<PackageIconUrl>http://go.microsoft.com/fwlink/?LinkID=288890</PackageIconUrl>
66
<PackageProjectUrl>https://github.com/Microsoft/OpenAPI.NET</PackageProjectUrl>
7-
<PackageLicenseUrl>https://raw.githubusercontent.com/Microsoft/OpenAPI.NET/master/LICENSE</PackageLicenseUrl>
7+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
88
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
99
<Authors>Microsoft</Authors>
1010
<Company>Microsoft</Company>
@@ -26,7 +26,7 @@
2626
</PropertyGroup>
2727

2828
<ItemGroup>
29-
<PackageReference Include="SharpYaml" Version="1.6.5" />
29+
<PackageReference Include="SharpYaml" Version="1.6.6" />
3030
</ItemGroup>
3131

3232
<ItemGroup>

src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
using Microsoft.OpenApi.Any;
55
using Microsoft.OpenApi.Interfaces;
6+
using Microsoft.OpenApi.Readers.Interface;
67
using Microsoft.OpenApi.Readers.ParseNodes;
8+
using Microsoft.OpenApi.Readers.Services;
79
using Microsoft.OpenApi.Validations;
810
using System;
911
using System.Collections.Generic;
12+
using System.IO;
1013
using System.Linq;
1114
using System.Text;
1215
using System.Threading.Tasks;
@@ -56,5 +59,13 @@ public class OpenApiReaderSettings
5659
/// URL where relative references should be resolved from if the description does not contain Server definitions
5760
/// </summary>
5861
public Uri BaseUrl { get; set; }
62+
63+
/// <summary>
64+
/// Function used to provide an alternative loader for accessing external references.
65+
/// </summary>
66+
/// <remarks>
67+
/// Default loader will attempt to dereference http(s) urls and file urls.
68+
/// </remarks>
69+
public IStreamLoader CustomExternalLoader { get; set; }
5970
}
6071
}

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System.IO;
5+
using System.Threading.Tasks;
56
using Microsoft.OpenApi.Interfaces;
67
using Microsoft.OpenApi.Models;
78
using Microsoft.OpenApi.Readers.Interface;
@@ -38,14 +39,40 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
3839
}
3940
}
4041

42+
/// <summary>
43+
/// Reads the stream input and parses it into an Open API document.
44+
/// </summary>
45+
/// <param name="input">Stream containing OpenAPI description to parse.</param>
46+
/// <returns>Instance result containing newly created OpenApiDocument and diagnostics object from the process</returns>
47+
public async Task<ReadResult> ReadAsync(Stream input)
48+
{
49+
MemoryStream bufferedStream;
50+
if (input is MemoryStream)
51+
{
52+
bufferedStream = (MemoryStream)input;
53+
}
54+
else
55+
{
56+
// Buffer stream so that OpenApiTextReaderReader can process it synchronously
57+
// YamlDocument doesn't support async reading.
58+
bufferedStream = new MemoryStream();
59+
await input.CopyToAsync(bufferedStream);
60+
bufferedStream.Position = 0;
61+
}
62+
63+
var reader = new StreamReader(bufferedStream);
64+
65+
return await new OpenApiTextReaderReader(_settings).ReadAsync(reader);
66+
}
67+
4168
/// <summary>
4269
/// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
4370
/// </summary>
4471
/// <param name="input">Stream containing OpenAPI description to parse.</param>
4572
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
4673
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
4774
/// <returns>Instance of newly created OpenApiDocument</returns>
48-
public T ReadFragment<T>(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
75+
public T ReadFragment<T>(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiReferenceable
4976
{
5077
using (var reader = new StreamReader(input))
5178
{

src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.IO;
55
using System.Linq;
6+
using System.Threading.Tasks;
67
using Microsoft.OpenApi.Interfaces;
78
using Microsoft.OpenApi.Models;
89
using Microsoft.OpenApi.Readers.Interface;
@@ -51,6 +52,36 @@ public OpenApiDocument Read(TextReader input, out OpenApiDiagnostic diagnostic)
5152

5253
return new OpenApiYamlDocumentReader(this._settings).Read(yamlDocument, out diagnostic);
5354
}
55+
56+
/// <summary>
57+
/// Reads the content of the TextReader. If there are references to external documents then they will be read asynchronously.
58+
/// </summary>
59+
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
60+
/// <returns>A ReadResult instance that contains the resulting OpenApiDocument and a diagnostics instance.</returns>
61+
public async Task<ReadResult> ReadAsync(TextReader input)
62+
{
63+
YamlDocument yamlDocument;
64+
65+
// Parse the YAML/JSON text in the TextReader into the YamlDocument
66+
try
67+
{
68+
yamlDocument = LoadYamlDocument(input);
69+
}
70+
catch (YamlException ex)
71+
{
72+
var diagnostic = new OpenApiDiagnostic();
73+
diagnostic.Errors.Add(new OpenApiError($"#line={ex.Start.Line}", ex.Message));
74+
return new ReadResult
75+
{
76+
OpenApiDocument = null,
77+
OpenApiDiagnostic = diagnostic
78+
};
79+
}
80+
81+
return await new OpenApiYamlDocumentReader(this._settings).ReadAsync(yamlDocument);
82+
}
83+
84+
5485
/// <summary>
5586
/// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
5687
/// </summary>

src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Threading.Tasks;
58
using Microsoft.OpenApi.Exceptions;
69
using Microsoft.OpenApi.Extensions;
710
using Microsoft.OpenApi.Interfaces;
@@ -50,24 +53,43 @@ public OpenApiDocument Read(YamlDocument input, out OpenApiDiagnostic diagnostic
5053
// Parse the OpenAPI Document
5154
document = context.Parse(input);
5255

53-
// Resolve References if requested
54-
switch (_settings.ReferenceResolution)
56+
ResolveReferences(diagnostic, document);
57+
}
58+
catch (OpenApiException ex)
59+
{
60+
diagnostic.Errors.Add(new OpenApiError(ex));
61+
}
62+
63+
// Validate the document
64+
if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0)
65+
{
66+
var errors = document.Validate(_settings.RuleSet);
67+
foreach (var item in errors)
5568
{
56-
case ReferenceResolutionSetting.ResolveAllReferences:
57-
throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously);
58-
case ReferenceResolutionSetting.ResolveLocalReferences:
59-
var resolver = new OpenApiReferenceResolver(document);
60-
var walker = new OpenApiWalker(resolver);
61-
walker.Walk(document);
62-
foreach (var item in resolver.Errors)
63-
{
64-
diagnostic.Errors.Add(item);
65-
}
66-
break;
67-
case ReferenceResolutionSetting.DoNotResolveReferences:
68-
break;
69+
diagnostic.Errors.Add(item);
6970
}
7071
}
72+
73+
return document;
74+
}
75+
76+
public async Task<ReadResult> ReadAsync(YamlDocument input)
77+
{
78+
var diagnostic = new OpenApiDiagnostic();
79+
var context = new ParsingContext(diagnostic)
80+
{
81+
ExtensionParsers = _settings.ExtensionParsers,
82+
BaseUrl = _settings.BaseUrl
83+
};
84+
85+
OpenApiDocument document = null;
86+
try
87+
{
88+
// Parse the OpenAPI Document
89+
document = context.Parse(input);
90+
91+
await ResolveReferencesAsync(diagnostic, document);
92+
}
7193
catch (OpenApiException ex)
7294
{
7395
diagnostic.Errors.Add(new OpenApiError(ex));
@@ -83,8 +105,71 @@ public OpenApiDocument Read(YamlDocument input, out OpenApiDiagnostic diagnostic
83105
}
84106
}
85107

86-
return document;
108+
return new ReadResult()
109+
{
110+
OpenApiDocument = document,
111+
OpenApiDiagnostic = diagnostic
112+
};
113+
}
114+
115+
116+
private void ResolveReferences(OpenApiDiagnostic diagnostic, OpenApiDocument document)
117+
{
118+
// Resolve References if requested
119+
switch (_settings.ReferenceResolution)
120+
{
121+
case ReferenceResolutionSetting.ResolveAllReferences:
122+
throw new ArgumentException("Cannot resolve all references via a synchronous call. Use ReadAsync.");
123+
case ReferenceResolutionSetting.ResolveLocalReferences:
124+
var errors = document.ResolveReferences(false);
125+
126+
foreach (var item in errors)
127+
{
128+
diagnostic.Errors.Add(item);
129+
}
130+
break;
131+
case ReferenceResolutionSetting.DoNotResolveReferences:
132+
break;
133+
}
87134
}
135+
136+
private async Task ResolveReferencesAsync(OpenApiDiagnostic diagnostic, OpenApiDocument document)
137+
{
138+
List<OpenApiError> errors = new List<OpenApiError>();
139+
140+
// Resolve References if requested
141+
switch (_settings.ReferenceResolution)
142+
{
143+
case ReferenceResolutionSetting.ResolveAllReferences:
144+
145+
// Create workspace for all documents to live in.
146+
var openApiWorkSpace = new OpenApiWorkspace();
147+
148+
// Load this root document into the workspace
149+
var streamLoader = new DefaultStreamLoader();
150+
var workspaceLoader = new OpenApiWorkspaceLoader(openApiWorkSpace, _settings.CustomExternalLoader ?? streamLoader, _settings);
151+
await workspaceLoader.LoadAsync(new OpenApiReference() { ExternalResource = "/" }, document);
152+
153+
// Resolve all references in all the documents loaded into the OpenApiWorkspace
154+
foreach (var doc in openApiWorkSpace.Documents)
155+
{
156+
errors.AddRange(doc.ResolveReferences(true));
157+
}
158+
break;
159+
case ReferenceResolutionSetting.ResolveLocalReferences:
160+
errors.AddRange(document.ResolveReferences(false));
161+
break;
162+
case ReferenceResolutionSetting.DoNotResolveReferences:
163+
break;
164+
}
165+
166+
foreach (var item in errors)
167+
{
168+
diagnostic.Errors.Add(item);
169+
}
170+
}
171+
172+
88173
/// <summary>
89174
/// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
90175
/// </summary>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Microsoft.OpenApi.Models;
10+
11+
namespace Microsoft.OpenApi.Readers
12+
{
13+
/// <summary>
14+
/// Container object used for returning the result of reading an OpenAPI description.
15+
/// </summary>
16+
public class ReadResult
17+
{
18+
/// <summary>
19+
/// The parsed OpenApiDocument. Null will be returned if the document could not be parsed.
20+
/// </summary>
21+
public OpenApiDocument OpenApiDocument { set; get; }
22+
/// <summary>
23+
/// OpenApiDiagnostic contains the Errors reported while parsing
24+
/// </summary>
25+
public OpenApiDiagnostic OpenApiDiagnostic { set; get; }
26+
}
27+
}

0 commit comments

Comments
 (0)