Skip to content

Commit 6f643b2

Browse files
committed
feat: adds node selection parameter for the walker
Signed-off-by: Vincent Biret <vibiret@microsoft.com>
1 parent 748b3d2 commit 6f643b2

File tree

3 files changed

+353
-20
lines changed

3 files changed

+353
-20
lines changed

src/Microsoft.OpenApi/Services/OpenApiWalker.cs

Lines changed: 74 additions & 20 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 System.Linq.Expressions;
78
using System.Net.Http;
89
using System.Text.Json.Nodes;
910

@@ -15,15 +16,56 @@ namespace Microsoft.OpenApi
1516
public class OpenApiWalker
1617
{
1718
private readonly OpenApiVisitorBase _visitor;
19+
private readonly Expression<Func<OpenApiDocument, object>>[]? _nodesToVisit;
1820
private readonly Stack<IOpenApiSchema> _schemaLoop = new();
1921
private readonly Stack<IOpenApiPathItem> _pathItemLoop = new();
2022

2123
/// <summary>
2224
/// Initializes the <see cref="OpenApiWalker"/> class.
2325
/// </summary>
24-
public OpenApiWalker(OpenApiVisitorBase visitor)
26+
/// <param name="visitor">The visitor to use for walking the OpenAPI document.</param>
27+
/// <param name="nodesToVisit">An expression specifying which nodes to visit.</param>
28+
/// <example>
29+
/// <code>
30+
/// var walker = new OpenApiWalker(visitor, [
31+
/// {
32+
/// doc =&gt; doc.Paths,
33+
/// doc =&gt; doc.Components.Schemas
34+
/// }]);
35+
/// </code>
36+
/// </example>
37+
public OpenApiWalker(OpenApiVisitorBase visitor, Expression<Func<OpenApiDocument, object>>[]? nodesToVisit)
2538
{
2639
_visitor = visitor;
40+
_nodesToVisit = nodesToVisit;
41+
}
42+
43+
/// <summary>
44+
/// Initializes the <see cref="OpenApiWalker"/> class.
45+
/// </summary>
46+
/// <param name="visitor">The visitor to use for walking the OpenAPI document.</param>
47+
public OpenApiWalker(OpenApiVisitorBase visitor):this(visitor, null)
48+
{
49+
// TODO: remove this constructor in next major version and make nodesToVisit params
50+
}
51+
private HashSet<string>? nodesToVisitPaths;
52+
private bool IsNodeSelected(Expression<Func<OpenApiDocument, object>> candidate)
53+
{
54+
if (_nodesToVisit is not { Length: > 0 })
55+
{
56+
return true;
57+
}
58+
59+
if (nodesToVisitPaths == null || nodesToVisitPaths.Count != _nodesToVisit.Length)
60+
{
61+
nodesToVisitPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
62+
foreach (var node in _nodesToVisit)
63+
{
64+
nodesToVisitPaths.Add(node.ToString());
65+
}
66+
}
67+
68+
return nodesToVisitPaths.Contains(candidate.ToString());
2769
}
2870

2971
/// <summary>
@@ -42,39 +84,39 @@ public void Walk(OpenApiDocument? doc)
4284

4385
_visitor.Visit(doc);
4486

45-
if (doc.Info is { } info)
87+
if (doc.Info is { } info && IsNodeSelected(static d => d.Info))
4688
{
4789
WalkItem(OpenApiConstants.Info, info, static (self, item) => self.Walk(item));
4890
}
4991

50-
if (doc.Servers is { } servers)
92+
if (doc.Servers is { } servers && IsNodeSelected(static d => d.Servers!))
5193
{
5294
WalkItem(OpenApiConstants.Servers, servers, static (self, item) => self.Walk(item));
5395
}
5496

55-
if (doc.Paths is { } paths)
97+
if (doc.Paths is { } paths && IsNodeSelected(static d => d.Paths))
5698
{
5799
WalkItem(OpenApiConstants.Paths, paths, static (self, item) => self.Walk(item));
58100
}
59101

60102
WalkDictionary(OpenApiConstants.Webhooks, doc.Webhooks, static (self, item, isComponent) => self.Walk(item, isComponent));
61103

62-
if (doc.Components is { } components)
104+
if (doc.Components is { } components && IsNodeSelected(static d => d.Components!))
63105
{
64106
WalkItem(OpenApiConstants.Components, components, static (self, item) => self.Walk(item));
65107
}
66108

67-
if (doc.Security is { } security)
109+
if (doc.Security is { } security && IsNodeSelected(static d => d.Security!))
68110
{
69111
WalkItem(OpenApiConstants.Security, security, static (self, item) => self.Walk(item));
70112
}
71113

72-
if (doc.ExternalDocs is { } externalDocs)
114+
if (doc.ExternalDocs is { } externalDocs && IsNodeSelected(static d => d.ExternalDocs!))
73115
{
74116
WalkItem(OpenApiConstants.ExternalDocs, externalDocs, static (self, item) => self.Walk(item));
75117
}
76118

77-
if (doc.Tags is { } tags)
119+
if (doc.Tags is { } tags && IsNodeSelected(static d => d.Tags!))
78120
{
79121
WalkItem(OpenApiConstants.Tags, tags, static (self, item) => self.Walk(item));
80122
}
@@ -151,18 +193,30 @@ internal void Walk(OpenApiComponents? components)
151193
_visitor.Visit(components);
152194

153195
var isComponent = true;
154-
WalkDictionary(OpenApiConstants.Schemas, components.Schemas, static (self, item, isComponent) => self.Walk(item), isComponent);
155-
WalkDictionary(OpenApiConstants.SecuritySchemes, components.SecuritySchemes, static (self, item, isComponent) => self.Walk(item), isComponent);
156-
WalkDictionary(OpenApiConstants.Callbacks, components.Callbacks, static (self, item, isComponent) => self.Walk(item), isComponent);
157-
WalkDictionary(OpenApiConstants.PathItems, components.PathItems, static (self, item, isComponent) => self.Walk(item), isComponent);
158-
WalkDictionary(OpenApiConstants.Parameters, components.Parameters, static (self, item, isComponent) => self.Walk(item), isComponent);
159-
WalkDictionary(OpenApiConstants.Examples, components.Examples, static (self, item, isComponent) => self.Walk(item), isComponent);
160-
WalkDictionary(OpenApiConstants.Headers, components.Headers, static (self, item, isComponent) => self.Walk(item), isComponent);
161-
WalkDictionary(OpenApiConstants.Links, components.Links, static (self, item, isComponent) => self.Walk(item), isComponent);
162-
WalkDictionary(OpenApiConstants.RequestBodies, components.RequestBodies, static (self, item, isComponent) => self.Walk(item), isComponent);
163-
WalkDictionary(OpenApiConstants.Responses, components.Responses, static (self, item, isComponent) => self.Walk(item), isComponent);
164-
165-
Walk(components as IOpenApiExtensible);
196+
var isComponentsNodeSelected = IsNodeSelected(static d => d.Components!);
197+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Schemas!))
198+
WalkDictionary(OpenApiConstants.Schemas, components.Schemas, static (self, item, isComponent) => self.Walk(item), isComponent);
199+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.SecuritySchemes!))
200+
WalkDictionary(OpenApiConstants.SecuritySchemes, components.SecuritySchemes, static (self, item, isComponent) => self.Walk(item), isComponent);
201+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Callbacks!))
202+
WalkDictionary(OpenApiConstants.Callbacks, components.Callbacks, static (self, item, isComponent) => self.Walk(item), isComponent);
203+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.PathItems!))
204+
WalkDictionary(OpenApiConstants.PathItems, components.PathItems, static (self, item, isComponent) => self.Walk(item), isComponent);
205+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Parameters!))
206+
WalkDictionary(OpenApiConstants.Parameters, components.Parameters, static (self, item, isComponent) => self.Walk(item), isComponent);
207+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Examples!))
208+
WalkDictionary(OpenApiConstants.Examples, components.Examples, static (self, item, isComponent) => self.Walk(item), isComponent);
209+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Headers!))
210+
WalkDictionary(OpenApiConstants.Headers, components.Headers, static (self, item, isComponent) => self.Walk(item), isComponent);
211+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Links!))
212+
WalkDictionary(OpenApiConstants.Links, components.Links, static (self, item, isComponent) => self.Walk(item), isComponent);
213+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.RequestBodies!))
214+
WalkDictionary(OpenApiConstants.RequestBodies, components.RequestBodies, static (self, item, isComponent) => self.Walk(item), isComponent);
215+
if (isComponentsNodeSelected || IsNodeSelected(static d => d.Components!.Responses!))
216+
WalkDictionary(OpenApiConstants.Responses, components.Responses, static (self, item, isComponent) => self.Walk(item), isComponent);
217+
218+
if (isComponentsNodeSelected)
219+
Walk(components as IOpenApiExtensible);
166220
}
167221

168222
/// <summary>

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,6 +1535,7 @@ namespace Microsoft.OpenApi
15351535
public class OpenApiWalker
15361536
{
15371537
public OpenApiWalker(Microsoft.OpenApi.OpenApiVisitorBase visitor) { }
1538+
public OpenApiWalker(Microsoft.OpenApi.OpenApiVisitorBase visitor, System.Linq.Expressions.Expression<System.Func<Microsoft.OpenApi.OpenApiDocument, object>>[]? nodesToVisit) { }
15381539
public void Walk(Microsoft.OpenApi.OpenApiDocument? doc) { }
15391540
}
15401541
public class OpenApiWorkspace

0 commit comments

Comments
 (0)