Skip to content

Commit 4118028

Browse files
authored
Merge pull request #176 from Microsoft/dm/validatedextensions
Validation for custom extensions
2 parents 067ec94 + de7e355 commit 4118028

File tree

81 files changed

+1276
-373
lines changed

Some content is hidden

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

81 files changed

+1276
-373
lines changed

.vscode/tasks.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558
3+
// for the documentation about the tasks.json format
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"label": "build",
8+
"type": "shell",
9+
"command": "msbuild",
10+
"args": [
11+
"/property:GenerateFullPaths=true",
12+
"/t:build"
13+
],
14+
"group": "build",
15+
"presentation": {
16+
"reveal": "silent"
17+
},
18+
"problemMatcher": "$msCompile"
19+
},
20+
{
21+
"label": "workbench",
22+
"type": "shell",
23+
"command": "src/Microsoft.OpenApi.WorkBench/bin/Debug/Microsoft.OpenApi.WorkBench.exe",
24+
"problemMatcher": []
25+
}
26+
]
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.OpenApi.Any;
2+
using Microsoft.OpenApi.Interfaces;
3+
using Microsoft.OpenApi.Readers.ParseNodes;
4+
using Microsoft.OpenApi.Validations;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
11+
namespace Microsoft.OpenApi.Readers
12+
{
13+
/// <summary>
14+
/// Configuration settings to control how OpenAPI documents are parsed
15+
/// </summary>
16+
public class OpenApiReaderSettings
17+
{
18+
/// <summary>
19+
/// Dictionary of parsers for converting extensions into strongly typed classes
20+
/// </summary>
21+
public Dictionary<string, Func<IOpenApiAny , IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
22+
23+
/// <summary>
24+
/// Rules to use for validating OpenAPI specification. If none are provided a default set of rules are applied.
25+
/// </summary>
26+
public ValidationRuleSet RuleSet { get; set; }
27+
28+
}
29+
}

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
using System.IO;
55
using System.Linq;
6+
using Microsoft.OpenApi.Extensions;
67
using Microsoft.OpenApi.Models;
78
using Microsoft.OpenApi.Readers.Interface;
9+
using Microsoft.OpenApi.Services;
10+
using Microsoft.OpenApi.Validations;
811
using SharpYaml;
912
using SharpYaml.Serialization;
1013

@@ -15,7 +18,17 @@ namespace Microsoft.OpenApi.Readers
1518
/// </summary>
1619
public class OpenApiStreamReader : IOpenApiReader<Stream, OpenApiDiagnostic>
1720
{
21+
private OpenApiReaderSettings _settings;
1822

23+
/// <summary>
24+
/// Create stream reader with custom settings if desired.
25+
/// </summary>
26+
/// <param name="settings"></param>
27+
public OpenApiStreamReader(OpenApiReaderSettings settings = null)
28+
{
29+
_settings = settings ?? new OpenApiReaderSettings();
30+
31+
}
1932
/// <summary>
2033
/// Reads the stream input and parses it into an Open API document.
2134
/// </summary>
@@ -28,6 +41,7 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
2841
YamlDocument yamlDocument;
2942
diagnostic = new OpenApiDiagnostic();
3043

44+
// Parse the YAML/JSON
3145
try
3246
{
3347
yamlDocument = LoadYamlDocument(input);
@@ -38,8 +52,22 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
3852
return new OpenApiDocument();
3953
}
4054

41-
context = new ParsingContext();
42-
return context.Parse(yamlDocument, diagnostic);
55+
context = new ParsingContext
56+
{
57+
ExtensionParsers = _settings.ExtensionParsers
58+
};
59+
60+
// Parse the OpenAPI Document
61+
var document = context.Parse(yamlDocument, diagnostic);
62+
63+
// Validate the document
64+
var errors = document.Validate(_settings.RuleSet);
65+
foreach (var item in errors)
66+
{
67+
diagnostic.Errors.Add(new OpenApiError(item.ErrorPath, item.ErrorMessage));
68+
}
69+
70+
return document;
4371
}
4472

4573
/// <summary>

src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ namespace Microsoft.OpenApi.Readers
1212
/// </summary>
1313
public class OpenApiStringReader : IOpenApiReader<string, OpenApiDiagnostic>
1414
{
15+
private readonly OpenApiReaderSettings _settings;
16+
17+
/// <summary>
18+
/// Constructor tha allows reader to use non-default settings
19+
/// </summary>
20+
/// <param name="settings"></param>
21+
public OpenApiStringReader(OpenApiReaderSettings settings = null)
22+
{
23+
_settings = settings ?? new OpenApiReaderSettings();
24+
}
25+
1526
/// <summary>
1627
/// Reads the string input and parses it into an Open API document.
1728
/// </summary>
@@ -24,7 +35,7 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic)
2435
writer.Flush();
2536
memoryStream.Position = 0;
2637

27-
return new OpenApiStreamReader().Read(memoryStream, out diagnostic);
38+
return new OpenApiStreamReader(_settings).Read(memoryStream, out diagnostic);
2839
}
2940
}
3041
}

src/Microsoft.OpenApi.Readers/ParsingContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using Microsoft.OpenApi.Any;
78
using Microsoft.OpenApi.Interfaces;
89
using Microsoft.OpenApi.Models;
910
using Microsoft.OpenApi.Readers.Exceptions;
@@ -24,6 +25,9 @@ public class ParsingContext
2425
private readonly Dictionary<string, IOpenApiReferenceable> _referenceStore = new Dictionary<string, IOpenApiReferenceable>();
2526
private readonly Dictionary<string, object> _tempStorage = new Dictionary<string, object>();
2627
private IOpenApiVersionService _versionService;
28+
29+
internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
30+
2731
internal RootNode RootNode { get; set; }
2832
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();
2933

@@ -63,6 +67,7 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
6367
return doc;
6468
}
6569

70+
6671
/// <summary>
6772
/// Gets the version of the Open API document.
6873
/// </summary>

src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,8 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
125125

126126
var openApiNode = rootNode.GetMap();
127127

128-
var required = new List<string> {"info", "swagger", "paths"};
128+
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
129129

130-
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields, required);
131-
132-
ReportMissing(openApiNode, required);
133130

134131
// Post Process OpenApi Object
135132
if (openApidoc.Servers == null)

src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,8 @@ public static OpenApiInfo LoadInfo(ParseNode node)
6565
var mapNode = node.CheckMapNode("Info");
6666

6767
var info = new OpenApiInfo();
68-
var required = new List<string> {"title", "version"};
6968

70-
ParseMap(mapNode, info, _infoFixedFields, _infoPatternFields, required);
71-
72-
ReportMissing(node, required);
69+
ParseMap(mapNode, info, _infoFixedFields, _infoPatternFields);
7370

7471
return info;
7572
}

src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,6 @@ private static void ParseMap<T>(
3232
}
3333
}
3434

35-
private static void ReportMissing(ParseNode node, IList<string> required)
36-
{
37-
foreach (var error in required.Select(
38-
r => new OpenApiError(
39-
node.Context.GetLocation(),
40-
$"{r} is a required property"))
41-
.ToList())
42-
{
43-
node.Diagnostic.Errors.Add(error);
44-
}
45-
}
4635

4736
private static string LoadString(ParseNode node)
4837
{

src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs

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

44
using System.Collections.Generic;
5+
using Microsoft.OpenApi.Any;
56
using Microsoft.OpenApi.Extensions;
7+
using Microsoft.OpenApi.Interfaces;
68
using Microsoft.OpenApi.Models;
79
using Microsoft.OpenApi.Readers.ParseNodes;
810

@@ -42,13 +44,21 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
4244

4345
var openApiNode = rootNode.GetMap();
4446

45-
var required = new List<string> {"info", "openapi", "paths"};
47+
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
4648

47-
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields, required);
49+
return openApidoc;
50+
}
4851

49-
ReportMissing(openApiNode, required);
5052

51-
return openApidoc;
53+
public static IOpenApiExtension LoadExtension(string name, ParseNode node)
54+
{
55+
if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) {
56+
return parser(node.CreateAny());
57+
}
58+
else
59+
{
60+
return node.CreateAny();
61+
}
5262
}
5363
}
5464
}

src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ internal static partial class OpenApiV3Deserializer
5757

5858
public static PatternFieldMap<OpenApiInfo> InfoPatternFields = new PatternFieldMap<OpenApiInfo>
5959
{
60-
{s => s.StartsWith("x-"), (o, k, n) => o.AddExtension(k, n.CreateAny())}
60+
{s => s.StartsWith("x-"), (o, k, n) => o.Extensions.Add(k,LoadExtension(k, n))}
6161
};
6262

6363
public static OpenApiInfo LoadInfo(ParseNode node)
@@ -67,7 +67,7 @@ public static OpenApiInfo LoadInfo(ParseNode node)
6767
var info = new OpenApiInfo();
6868
var required = new List<string> {"title", "version"};
6969

70-
ParseMap(mapNode, info, InfoFixedFields, InfoPatternFields, required);
70+
ParseMap(mapNode, info, InfoFixedFields, InfoPatternFields);
7171

7272
return info;
7373
}

0 commit comments

Comments
 (0)