Skip to content

Commit 41b049d

Browse files
committed
Implemented extension validation
1 parent 60a26b2 commit 41b049d

26 files changed

+332
-142
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+
}

src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.OpenApi.Any;
22
using Microsoft.OpenApi.Interfaces;
33
using Microsoft.OpenApi.Readers.ParseNodes;
4+
using Microsoft.OpenApi.Validations;
45
using System;
56
using System.Collections.Generic;
67
using System.Linq;
@@ -19,5 +20,10 @@ public class OpenApiReaderSettings
1920
/// </summary>
2021
public Dictionary<string, Func<IOpenApiAny , IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
2122

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+
2228
}
2329
}

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
6161
var document = context.Parse(yamlDocument, diagnostic);
6262

6363
// Validate the document
64-
var errors = document.Validate();
64+
var errors = document.Validate(_settings.RuleSet);
6565
foreach (var item in errors)
6666
{
6767
diagnostic.Errors.Add(new OpenApiError(item.ErrorPath, item.ErrorMessage));

src/Microsoft.OpenApi/Extensions/OpenApiElementExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ public static class OpenApiElementExtensions
1818
/// <summary>
1919
/// Validate element and all child elements
2020
/// </summary>
21-
/// <typeparam name="T"></typeparam>
22-
/// <param name="element"></param>
21+
/// <param name="element">Element to validate</param>
22+
/// <param name="ruleSet">Optional set of rules to use for validation</param>
2323
/// <returns>An IEnumerable of errors. This function will never return null.</returns>
24-
public static IEnumerable<ValidationError> Validate(this IOpenApiElement element) {
25-
var validator = new OpenApiValidator();
24+
public static IEnumerable<ValidationError> Validate(this IOpenApiElement element, ValidationRuleSet ruleSet = null) {
25+
var validator = new OpenApiValidator(ruleSet);
2626
var walker = new OpenApiWalker(validator);
2727
walker.Walk(element);
2828
return validator.Errors;

src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
using System;
45
using System.Collections.Generic;
6+
using System.Linq;
57
using Microsoft.OpenApi.Interfaces;
68
using Microsoft.OpenApi.Models;
79

@@ -12,6 +14,37 @@ namespace Microsoft.OpenApi.Services
1214
/// </summary>
1315
public abstract class OpenApiVisitorBase
1416
{
17+
private readonly Stack<string> _path = new Stack<string>();
18+
19+
/// <summary>
20+
/// Allow Rule to indicate validation error occured a deeper context level.
21+
/// </summary>
22+
/// <param name="segment"></param>
23+
public void Enter(string segment)
24+
{
25+
this._path.Push(segment);
26+
}
27+
28+
/// <summary>
29+
/// Exit from path context elevel. Enter and Exit calls should be matched.
30+
/// </summary>
31+
public void Exit()
32+
{
33+
this._path.Pop();
34+
}
35+
36+
/// <summary>
37+
/// Pointer to source of validation error in document
38+
/// </summary>
39+
public string PathString
40+
{
41+
get
42+
{
43+
return "#/" + String.Join("/", _path.Reverse());
44+
}
45+
}
46+
47+
1548
/// <summary>
1649
/// Visits <see cref="OpenApiDocument"/>
1750
/// </summary>
@@ -244,5 +277,13 @@ public virtual void Visit(IList<OpenApiTag> openApiTags)
244277
public virtual void Visit(IOpenApiExtensible openApiExtensible)
245278
{
246279
}
280+
281+
/// <summary>
282+
/// Visits <see cref="IOpenApiExtension"/>
283+
/// </summary>
284+
public virtual void Visit(IOpenApiExtension openApiExtension)
285+
{
286+
}
287+
247288
}
248289
}

src/Microsoft.OpenApi/Services/OpenApiWalker.cs

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Models;
77
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Any;
9+
using Microsoft.OpenApi.Extensions;
810

911
namespace Microsoft.OpenApi.Services
1012
{
@@ -46,12 +48,15 @@ public void Walk(OpenApiDocument doc)
4648
/// <param name="tags"></param>
4749
internal void Walk(IList<OpenApiTag> tags)
4850
{
51+
_visitor.Enter(OpenApiConstants.Tags);
4952
_visitor.Visit(tags);
5053

5154
foreach (var tag in tags)
5255
{
5356
Walk(tag);
5457
}
58+
_visitor.Exit();
59+
5560
}
5661

5762
/// <summary>
@@ -60,7 +65,9 @@ internal void Walk(IList<OpenApiTag> tags)
6065
/// <param name="externalDocs"></param>
6166
internal void Walk(OpenApiExternalDocs externalDocs)
6267
{
68+
_visitor.Enter(OpenApiConstants.ExternalDocs);
6369
_visitor.Visit(externalDocs);
70+
_visitor.Exit();
6471
}
6572

6673
/// <summary>
@@ -69,7 +76,9 @@ internal void Walk(OpenApiExternalDocs externalDocs)
6976
/// <param name="components"></param>
7077
internal void Walk(OpenApiComponents components)
7178
{
79+
_visitor.Enter(OpenApiConstants.Components);
7280
_visitor.Visit(components);
81+
_visitor.Exit();
7382
}
7483

7584
/// <summary>
@@ -78,11 +87,15 @@ internal void Walk(OpenApiComponents components)
7887
/// <param name="paths"></param>
7988
internal void Walk(OpenApiPaths paths)
8089
{
90+
_visitor.Enter(OpenApiConstants.Paths);
8191
_visitor.Visit(paths);
82-
foreach (var pathItem in paths.Values)
92+
foreach (var pathItem in paths)
8393
{
84-
Walk(pathItem);
94+
_visitor.Enter(pathItem.Key.Replace("/","~1"));
95+
Walk(pathItem.Value);
96+
_visitor.Exit();
8597
}
98+
_visitor.Exit();
8699
}
87100

88101
/// <summary>
@@ -91,6 +104,7 @@ internal void Walk(OpenApiPaths paths)
91104
/// <param name="servers"></param>
92105
internal void Walk(IList<OpenApiServer> servers)
93106
{
107+
_visitor.Enter(OpenApiConstants.Servers);
94108
_visitor.Visit(servers);
95109

96110
// Visit Servers
@@ -101,6 +115,7 @@ internal void Walk(IList<OpenApiServer> servers)
101115
Walk(server);
102116
}
103117
}
118+
_visitor.Exit();
104119
}
105120

106121
/// <summary>
@@ -109,10 +124,12 @@ internal void Walk(IList<OpenApiServer> servers)
109124
/// <param name="info"></param>
110125
internal void Walk(OpenApiInfo info)
111126
{
127+
_visitor.Enter(OpenApiConstants.Info);
112128
_visitor.Visit(info);
113129
Walk(info.Contact);
114130
Walk(info.License);
115131
Walk(info as IOpenApiExtensible);
132+
_visitor.Exit();
116133
}
117134

118135
/// <summary>
@@ -122,6 +139,21 @@ internal void Walk(OpenApiInfo info)
122139
internal void Walk(IOpenApiExtensible openApiExtensible)
123140
{
124141
_visitor.Visit(openApiExtensible);
142+
foreach (var item in openApiExtensible.Extensions)
143+
{
144+
_visitor.Enter(item.Key);
145+
Walk(item.Value);
146+
_visitor.Exit();
147+
}
148+
}
149+
150+
/// <summary>
151+
/// Visits <see cref="IOpenApiExtension"/>
152+
/// </summary>
153+
/// <param name="extension"></param>
154+
internal void Walk(IOpenApiExtension extension)
155+
{
156+
_visitor.Visit(extension);
125157
}
126158

127159
/// <summary>
@@ -130,7 +162,9 @@ internal void Walk(IOpenApiExtensible openApiExtensible)
130162
/// <param name="license"></param>
131163
internal void Walk(OpenApiLicense license)
132164
{
165+
_visitor.Enter(OpenApiConstants.License);
133166
_visitor.Visit(license);
167+
_visitor.Exit();
134168
}
135169

136170
/// <summary>
@@ -139,7 +173,9 @@ internal void Walk(OpenApiLicense license)
139173
/// <param name="contact"></param>
140174
internal void Walk(OpenApiContact contact)
141175
{
176+
_visitor.Enter(OpenApiConstants.Contact);
142177
_visitor.Visit(contact);
178+
_visitor.Exit();
143179
}
144180

145181
/// <summary>
@@ -159,12 +195,25 @@ internal void Walk(OpenApiTag tag)
159195
/// <param name="server"></param>
160196
internal void Walk(OpenApiServer server)
161197
{
198+
_visitor.Enter(OpenApiConstants.Server);
162199
_visitor.Visit(server);
163-
foreach (var variable in server.Variables.Values)
200+
Walk(server.Variables);
201+
_visitor.Visit(server as IOpenApiExtensible);
202+
_visitor.Exit();
203+
}
204+
205+
/// <summary>
206+
/// Visits dictionary of <see cref="OpenApiServerVariable"/>
207+
/// </summary>
208+
/// <param name="serverVariables"></param>
209+
internal void Walk(IDictionary<string,OpenApiServerVariable> serverVariables)
210+
{
211+
foreach (var variable in serverVariables)
164212
{
165-
Walk(variable);
213+
_visitor.Enter(variable.Key);
214+
Walk(variable.Value);
215+
_visitor.Exit();
166216
}
167-
_visitor.Visit(server as IOpenApiExtensible);
168217
}
169218

170219
/// <summary>
@@ -184,7 +233,6 @@ internal void Walk(OpenApiServerVariable serverVariable)
184233
internal void Walk(OpenApiPathItem pathItem)
185234
{
186235
_visitor.Visit(pathItem);
187-
188236
Walk(pathItem.Operations);
189237
_visitor.Visit(pathItem as IOpenApiExtensible);
190238
}
@@ -196,9 +244,11 @@ internal void Walk(OpenApiPathItem pathItem)
196244
internal void Walk(IDictionary<OperationType, OpenApiOperation> operations)
197245
{
198246
_visitor.Visit(operations);
199-
foreach (var operation in operations.Values)
247+
foreach (var operation in operations)
200248
{
201-
Walk(operation);
249+
_visitor.Enter(operation.Key.GetDisplayName());
250+
Walk(operation.Value);
251+
_visitor.Exit();
202252
}
203253
}
204254

@@ -224,11 +274,13 @@ internal void Walk(IList<OpenApiParameter> parameters)
224274
{
225275
if (parameters != null)
226276
{
277+
_visitor.Enter(OpenApiConstants.Parameters);
227278
_visitor.Visit(parameters);
228279
foreach (var parameter in parameters)
229280
{
230281
Walk(parameter);
231282
}
283+
_visitor.Exit();
232284
}
233285
}
234286

@@ -252,14 +304,18 @@ internal void Walk(OpenApiResponses responses)
252304
{
253305
if (responses != null)
254306
{
307+
_visitor.Enter(OpenApiConstants.Responses);
255308
_visitor.Visit(responses);
256309

257-
foreach (var response in responses.Values)
310+
foreach (var response in responses)
258311
{
259-
Walk(response);
312+
_visitor.Enter(response.Key);
313+
Walk(response.Value);
314+
_visitor.Exit();
260315
}
261316

262317
Walk(responses as IOpenApiExtensible);
318+
_visitor.Exit();
263319
}
264320
}
265321

@@ -314,11 +370,15 @@ internal void Walk(IDictionary<string, OpenApiMediaType> content)
314370
return;
315371
}
316372

373+
_visitor.Enter(OpenApiConstants.Content);
317374
_visitor.Visit(content);
318-
foreach (var mediaType in content.Values)
375+
foreach (var mediaType in content)
319376
{
320-
Walk(mediaType);
377+
_visitor.Enter(mediaType.Key);
378+
Walk(mediaType.Value);
379+
_visitor.Exit();
321380
}
381+
_visitor.Exit();
322382
}
323383

324384
/// <summary>
@@ -381,6 +441,15 @@ internal void Walk(IDictionary<string,OpenApiExample> examples)
381441
}
382442
}
383443

444+
/// <summary>
445+
/// Visits <see cref="IOpenApiAny"/> and child objects
446+
/// </summary>
447+
/// <param name="example"></param>
448+
internal void Walk(IOpenApiAny example)
449+
{
450+
_visitor.Visit(example);
451+
}
452+
384453
/// <summary>
385454
/// Visits <see cref="OpenApiExample"/> and child objects
386455
/// </summary>
@@ -517,6 +586,8 @@ internal void Walk(IOpenApiElement element)
517586
case IList<OpenApiTag> e: Walk(e); break;
518587
case OpenApiXml e: Walk(e); break;
519588
case IOpenApiExtensible e: Walk(e); break;
589+
case IOpenApiExtension e: Walk(e); break;
590+
520591
}
521592
}
522593

0 commit comments

Comments
 (0)