Skip to content

Commit a956d83

Browse files
committed
Merge branch 'vnext' into feature/ci-cd
2 parents 2e6219b + 9385ffe commit a956d83

File tree

10 files changed

+775
-12
lines changed

10 files changed

+775
-12
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ internal static partial class OpenApiV2Deserializer
2626
{
2727
"multipleOf", (o, n) =>
2828
{
29-
o.MultipleOf = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
29+
o.MultipleOf = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
3030
}
3131
},
3232
{
3333
"maximum", (o, n) =>
3434
{
35-
o.Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
35+
o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
3636
}
3737
},
3838
{
@@ -44,7 +44,7 @@ internal static partial class OpenApiV2Deserializer
4444
{
4545
"minimum", (o, n) =>
4646
{
47-
o.Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
47+
o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
4848
}
4949
},
5050
{

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal static partial class OpenApiV3Deserializer
3333
{
3434
"maximum", (o, n) =>
3535
{
36-
o.Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
36+
o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
3737
}
3838
},
3939
{
@@ -45,7 +45,7 @@ internal static partial class OpenApiV3Deserializer
4545
{
4646
"minimum", (o, n) =>
4747
{
48-
o.Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture);
48+
o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
4949
}
5050
},
5151
{

src/Microsoft.OpenApi/Extensions/StringExtensions.cs

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

44
using System;
55
using System.Reflection;
@@ -21,7 +21,7 @@ public static T GetEnumFromDisplayName<T>(this string displayName)
2121
var type = typeof(T);
2222
if (!type.IsEnum)
2323
{
24-
return default(T);
24+
return default;
2525
}
2626

2727
foreach (var value in Enum.GetValues(type))
@@ -35,7 +35,7 @@ public static T GetEnumFromDisplayName<T>(this string displayName)
3535
}
3636
}
3737

38-
return default(T);
38+
return default;
3939
}
4040
}
4141
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
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 Microsoft.OpenApi.Models;
8+
9+
namespace Microsoft.OpenApi.Services
10+
{
11+
/// <summary>
12+
/// A directory structure representing the paths of an OpenAPI document.
13+
/// </summary>
14+
public class OpenApiUrlTreeNode
15+
{
16+
private const string RootPathSegment = "/";
17+
private const string PathSeparator = "\\";
18+
19+
/// <summary>
20+
/// All the subdirectories of a node.
21+
/// </summary>
22+
public IDictionary<string, OpenApiUrlTreeNode> Children { get; } = new Dictionary<string, OpenApiUrlTreeNode>();
23+
24+
/// <summary>
25+
/// The relative directory path of the current node from the root node.
26+
/// </summary>
27+
public string Path { get; private set; } = "";
28+
29+
/// <summary>
30+
/// Dictionary of labels and Path Item objects that describe the operations available on a node.
31+
/// </summary>
32+
public IDictionary<string, OpenApiPathItem> PathItems { get; } = new Dictionary<string, OpenApiPathItem>();
33+
34+
/// <summary>
35+
/// A dictionary of key value pairs that contain information about a node.
36+
/// </summary>
37+
public IDictionary<string, List<string>> AdditionalData { get; set; } = new Dictionary<string, List<string>>();
38+
39+
/// <summary>
40+
/// Flag indicating whether a node segment is a path parameter.
41+
/// </summary>
42+
public bool IsParameter => Segment.StartsWith("{");
43+
44+
/// <summary>
45+
/// The subdirectory of a relative path.
46+
/// </summary>
47+
public string Segment { get; private set; }
48+
49+
/// <summary>
50+
/// Flag indicating whether the node's PathItems has operations.
51+
/// </summary>
52+
/// <returns>true or false.</returns>
53+
public bool HasOperations(string label)
54+
{
55+
if (!(PathItems?.ContainsKey(label) ?? false))
56+
{
57+
return false;
58+
}
59+
60+
return PathItems[label].Operations?.Any() ?? false;
61+
}
62+
63+
/// <summary>
64+
/// Constructor.
65+
/// </summary>
66+
/// <param name="segment">The subdirectory of a relative path.</param>
67+
private OpenApiUrlTreeNode(string segment)
68+
{
69+
Segment = segment;
70+
}
71+
72+
/// <summary>
73+
/// Creates an empty structured directory of <see cref="OpenApiUrlTreeNode"/> node.
74+
/// </summary>
75+
/// <returns>The root node of the created <see cref="OpenApiUrlTreeNode"/> directory structure.</returns>
76+
public static OpenApiUrlTreeNode Create()
77+
{
78+
return new OpenApiUrlTreeNode(RootPathSegment);
79+
}
80+
81+
/// <summary>
82+
/// Creates a structured directory of <see cref="OpenApiUrlTreeNode"/> nodes from the paths of an OpenAPI document.
83+
/// </summary>
84+
/// <param name="doc">The OpenAPI document.</param>
85+
/// <param name="label">Name tag for labelling the <see cref="OpenApiUrlTreeNode"/> nodes in the directory structure.</param>
86+
/// <returns>The root node of the created <see cref="OpenApiUrlTreeNode"/> directory structure.</returns>
87+
public static OpenApiUrlTreeNode Create(OpenApiDocument doc, string label)
88+
{
89+
Utils.CheckArgumentNull(doc, nameof(doc));
90+
Utils.CheckArgumentNullOrEmpty(label, nameof(label));
91+
92+
var root = Create();
93+
94+
var paths = doc.Paths;
95+
if (paths != null)
96+
{
97+
foreach (var path in paths)
98+
{
99+
root.Attach(path: path.Key,
100+
pathItem: path.Value,
101+
label: label);
102+
}
103+
}
104+
105+
return root;
106+
}
107+
108+
/// <summary>
109+
/// Retrieves the paths from an OpenAPI document and appends the items to an <see cref="OpenApiUrlTreeNode"/> node.
110+
/// </summary>
111+
/// <param name="doc">The OpenAPI document.</param>
112+
/// <param name="label">Name tag for labelling related <see cref="OpenApiUrlTreeNode"/> nodes in the directory structure.</param>
113+
public void Attach(OpenApiDocument doc, string label)
114+
{
115+
Utils.CheckArgumentNull(doc, nameof(doc));
116+
Utils.CheckArgumentNullOrEmpty(label, nameof(label));
117+
118+
var paths = doc.Paths;
119+
if (paths != null)
120+
{
121+
foreach (var path in paths)
122+
{
123+
Attach(path: path.Key,
124+
pathItem: path.Value,
125+
label: label);
126+
}
127+
}
128+
}
129+
130+
/// <summary>
131+
/// Appends a path and the PathItem to an <see cref="OpenApiUrlTreeNode"/> node.
132+
/// </summary>
133+
/// <param name="path">An OpenAPI path.</param>
134+
/// <param name="pathItem">Path Item object that describes the operations available on an OpenAPI path.</param>
135+
/// <param name="label">A name tag for labelling the <see cref="OpenApiUrlTreeNode"/> node.</param>
136+
/// <returns>An <see cref="OpenApiUrlTreeNode"/> node describing an OpenAPI path.</returns>
137+
public OpenApiUrlTreeNode Attach(string path,
138+
OpenApiPathItem pathItem,
139+
string label)
140+
{
141+
Utils.CheckArgumentNullOrEmpty(label, nameof(label));
142+
143+
if (path.StartsWith(RootPathSegment))
144+
{
145+
// Remove leading slash
146+
path = path.Substring(1);
147+
}
148+
149+
var segments = path.Split('/');
150+
151+
return Attach(segments: segments,
152+
pathItem: pathItem,
153+
label: label,
154+
currentPath: "");
155+
}
156+
157+
/// <summary>
158+
/// Assembles the constituent properties of an <see cref="OpenApiUrlTreeNode"/> node.
159+
/// </summary>
160+
/// <param name="segments">IEnumerable subdirectories of a relative path.</param>
161+
/// <param name="pathItem">Path Item object that describes the operations available on an OpenAPI path.</param>
162+
/// <param name="label">A name tag for labelling the <see cref="OpenApiUrlTreeNode"/> node.</param>
163+
/// <param name="currentPath">The relative path of a node.</param>
164+
/// <returns>An <see cref="OpenApiUrlTreeNode"/> node with all constituent properties assembled.</returns>
165+
private OpenApiUrlTreeNode Attach(IEnumerable<string> segments,
166+
OpenApiPathItem pathItem,
167+
string label,
168+
string currentPath)
169+
{
170+
var segment = segments.FirstOrDefault();
171+
if (string.IsNullOrEmpty(segment))
172+
{
173+
if (PathItems.ContainsKey(label))
174+
{
175+
throw new ArgumentException("A duplicate label already exists for this node.", nameof(label));
176+
}
177+
178+
Path = currentPath;
179+
PathItems.Add(label, pathItem);
180+
return this;
181+
}
182+
183+
// If the child segment has already been defined, then insert into it
184+
if (Children.ContainsKey(segment))
185+
{
186+
var newPath = currentPath + PathSeparator + segment;
187+
188+
return Children[segment].Attach(segments: segments.Skip(1),
189+
pathItem: pathItem,
190+
label: label,
191+
currentPath: newPath);
192+
}
193+
else
194+
{
195+
var newPath = currentPath + PathSeparator + segment;
196+
197+
var node = new OpenApiUrlTreeNode(segment)
198+
{
199+
Path = newPath
200+
};
201+
202+
Children[segment] = node;
203+
204+
return node.Attach(segments: segments.Skip(1),
205+
pathItem: pathItem,
206+
label: label,
207+
currentPath: newPath);
208+
}
209+
}
210+
211+
/// <summary>
212+
/// Adds additional data information to the AdditionalData property of the node.
213+
/// </summary>
214+
/// <param name="additionalData">A dictionary of key value pairs that contain information about a node.</param>
215+
public void AddAdditionalData(Dictionary<string, List<string>> additionalData)
216+
{
217+
Utils.CheckArgumentNull(additionalData, nameof(additionalData));
218+
219+
foreach (var item in additionalData)
220+
{
221+
if (AdditionalData.ContainsKey(item.Key))
222+
{
223+
AdditionalData[item.Key] = item.Value;
224+
}
225+
else
226+
{
227+
AdditionalData.Add(item.Key, item.Value);
228+
}
229+
}
230+
}
231+
}
232+
}

src/Microsoft.OpenApi/Utils.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.OpenApi
7+
{
8+
/// <summary>
9+
/// Utilities methods
10+
/// </summary>
11+
internal static class Utils
12+
{
13+
/// <summary>
14+
/// Check whether the input argument value is null or not.
15+
/// </summary>
16+
/// <typeparam name="T">The input value type.</typeparam>
17+
/// <param name="value">The input value.</param>
18+
/// <param name="parameterName">The input parameter name.</param>
19+
/// <returns>The input value.</returns>
20+
internal static T CheckArgumentNull<T>(T value, string parameterName) where T : class
21+
{
22+
return value ?? throw new ArgumentNullException(parameterName, $"Value cannot be null: {parameterName}");
23+
}
24+
25+
/// <summary>
26+
/// Check whether the input string value is null or empty.
27+
/// </summary>
28+
/// <param name="value">The input string value.</param>
29+
/// <param name="parameterName">The input parameter name.</param>
30+
/// <returns>The input value.</returns>
31+
internal static string CheckArgumentNullOrEmpty(string value, string parameterName)
32+
{
33+
return string.IsNullOrEmpty(value) ? throw new ArgumentNullException(parameterName, $"Value cannot be null or empty: {parameterName}") : value;
34+
}
35+
}
36+
}

test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
9696
sampleProperty:
9797
type: double
9898
minimum: 100.54
99-
maximum: 60,000,000.35
99+
maximum: 60000000.35
100100
exclusiveMaximum: true
101101
exclusiveMinimum: false
102102
paths: {}",

test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
8282
sampleProperty:
8383
type: double
8484
minimum: 100.54
85-
maximum: 60,000,000.35
85+
maximum: 60000000.35
8686
exclusiveMaximum: true
8787
exclusiveMinimum: false
8888
paths: {}",

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,21 @@ namespace Microsoft.OpenApi.Services
958958
public OpenApiReferenceError(Microsoft.OpenApi.Exceptions.OpenApiException exception) { }
959959
public OpenApiReferenceError(Microsoft.OpenApi.Models.OpenApiReference reference, string message) { }
960960
}
961+
public class OpenApiUrlTreeNode
962+
{
963+
public System.Collections.Generic.IDictionary<string, System.Collections.Generic.List<string>> AdditionalData { get; set; }
964+
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.Services.OpenApiUrlTreeNode> Children { get; }
965+
public bool IsParameter { get; }
966+
public string Path { get; }
967+
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.Models.OpenApiPathItem> PathItems { get; }
968+
public string Segment { get; }
969+
public void AddAdditionalData(System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> additionalData) { }
970+
public void Attach(Microsoft.OpenApi.Models.OpenApiDocument doc, string label) { }
971+
public Microsoft.OpenApi.Services.OpenApiUrlTreeNode Attach(string path, Microsoft.OpenApi.Models.OpenApiPathItem pathItem, string label) { }
972+
public bool HasOperations(string label) { }
973+
public static Microsoft.OpenApi.Services.OpenApiUrlTreeNode Create() { }
974+
public static Microsoft.OpenApi.Services.OpenApiUrlTreeNode Create(Microsoft.OpenApi.Models.OpenApiDocument doc, string label) { }
975+
}
961976
public abstract class OpenApiVisitorBase
962977
{
963978
protected OpenApiVisitorBase() { }

test/Microsoft.OpenApi.Tests/PublicApi/PublicApiTests.cs

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

44
using System.IO;
55
using Xunit;
@@ -27,7 +27,7 @@ public void ReviewPublicApiChanges()
2727

2828
// Arrange
2929
var publicApi = typeof(OpenApiSpecVersion).Assembly.GeneratePublicApi(new ApiGeneratorOptions() { WhitelistedNamespacePrefixes = new[] { "Microsoft.OpenApi" } } );
30-
30+
3131
// Act
3232
var approvedFilePath = Path.Combine("PublicApi", "PublicApi.approved.txt");
3333

0 commit comments

Comments
 (0)