Skip to content

Commit a768b9d

Browse files
committed
Init OpenApiUrlSpaceNode and tests
1 parent 4f4c12f commit a768b9d

File tree

3 files changed

+469
-3
lines changed

3 files changed

+469
-3
lines changed

src/Microsoft.OpenApi/Extensions/StringExtensions.cs

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

44
using System;
5+
using System.Linq;
56
using System.Reflection;
67
using Microsoft.OpenApi.Attributes;
78

@@ -21,7 +22,7 @@ public static T GetEnumFromDisplayName<T>(this string displayName)
2122
var type = typeof(T);
2223
if (!type.IsEnum)
2324
{
24-
return default(T);
25+
return default;
2526
}
2627

2728
foreach (var value in Enum.GetValues(type))
@@ -35,7 +36,23 @@ public static T GetEnumFromDisplayName<T>(this string displayName)
3536
}
3637
}
3738

38-
return default(T);
39+
return default;
3940
}
41+
42+
/// <summary>
43+
/// Capitalizes each letter of a word in a string.
44+
/// </summary>
45+
/// <param name="input">String containing the words to be capitalized, delimited by the '-' character.</param>
46+
/// <returns>String value with each word capitalized and concatenated.</returns>
47+
public static string ToPascalCase(this string input)
48+
=> string.IsNullOrEmpty(input) ? input : string.Join(null, input.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries)
49+
.Select(s => ToFirstCharacterUpperCase(s)));
50+
/// <summary>
51+
/// Capitalizes the first letter of an input string.
52+
/// </summary>
53+
/// <param name="input">String with first letter to be capitalized. </param>
54+
/// <returns>The string value with the first letter capitalized.</returns>
55+
public static string ToFirstCharacterUpperCase(this string input)
56+
=> string.IsNullOrEmpty(input) ? input : char.ToUpperInvariant(input.FirstOrDefault()) + input.Substring(1);
4057
}
4158
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Security.Cryptography;
7+
using System.Text;
8+
using Microsoft.OpenApi.Extensions;
9+
using Microsoft.OpenApi.Models;
10+
11+
namespace Microsoft.OpenApi.Services
12+
{
13+
/// <summary>
14+
/// A directory structure representing the paths of an OpenAPI document.
15+
/// </summary>
16+
public class OpenApiUrlSpaceNode
17+
{
18+
/// <summary>
19+
/// All the subdirectories of a node.
20+
/// </summary>
21+
public IDictionary<string, OpenApiUrlSpaceNode> Children { get; set; } = new Dictionary<string, OpenApiUrlSpaceNode>();
22+
23+
/// <summary>
24+
/// The name tag for a group of nodes.
25+
/// </summary>
26+
public string Label { get; set; }
27+
28+
/// <summary>
29+
/// Path Item object that describes the operations available on a node.
30+
/// </summary>
31+
public OpenApiPathItem PathItem { get; private set; }
32+
33+
/// <summary>
34+
/// The relative directory path of the current node from the root node.
35+
/// </summary>
36+
public string Path { get; set; } = "";
37+
38+
/// <summary>
39+
/// Flag indicating whether a node segment is a path parameter.
40+
/// </summary>
41+
public bool IsParameter => Segment.StartsWith("{");
42+
43+
/// <summary>
44+
/// Flag indicating whether a node segment is a path function.
45+
/// </summary>
46+
public bool IsFunction => Segment.Contains("(");
47+
48+
/// <summary>
49+
/// The subdirectory of a relative path.
50+
/// </summary>
51+
public string Segment { get; private set; }
52+
53+
/// <summary>
54+
/// The Pascal-cased alphabet name of a segment.
55+
/// </summary>
56+
public string Identifier
57+
{
58+
get
59+
{
60+
string identifier;
61+
if (IsParameter)
62+
{
63+
identifier = Segment.Substring(1, Segment.Length - 2).ToPascalCase();
64+
}
65+
else
66+
{
67+
identifier = Segment.ToPascalCase().Replace("()", "");
68+
var openParen = identifier.IndexOf("(");
69+
if (openParen >= 0)
70+
{
71+
identifier = identifier.Substring(0, openParen);
72+
}
73+
}
74+
return identifier;
75+
}
76+
}
77+
78+
/// <summary>
79+
/// Flag indicating whether a PathItem has operations.
80+
/// </summary>
81+
/// <returns>true or false.</returns>
82+
public bool HasOperations()
83+
{
84+
return PathItem != null && PathItem.Operations != null && PathItem.Operations.Count > 0;
85+
}
86+
87+
/// <summary>
88+
/// Constructor.
89+
/// </summary>
90+
/// <param name="segment">The subdirectory of a relative path.</param>
91+
public OpenApiUrlSpaceNode(string segment)
92+
{
93+
Segment = segment;
94+
}
95+
96+
/// <summary>
97+
/// Uses SHA256 hash algorithm to hash the Path value.
98+
/// </summary>
99+
/// <returns>The hashed value.</returns>
100+
public string Hash()
101+
{
102+
using (SHA256 sha256Hash = SHA256.Create())
103+
{
104+
return GetHash(sha256Hash, Path);
105+
}
106+
}
107+
108+
/// <summary>
109+
/// Hashes a string value using a specified hash algorithm.
110+
/// </summary>
111+
/// <param name="hashAlgorithm">The hash algorithm to use for hashing.</param>
112+
/// <param name="input">The string to hash.</param>
113+
/// <returns>The hashed value.</returns>
114+
private static string GetHash(HashAlgorithm hashAlgorithm, string input)
115+
{
116+
// Convert the input string to a byte array and compute the hash.
117+
byte[] data = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
118+
119+
// Create a new Stringbuilder to collect the bytes
120+
// and create a string.
121+
var sBuilder = new StringBuilder();
122+
123+
// Loop through each byte of the hashed data
124+
// and format each one as a hexadecimal string.
125+
for (int i = 0; i < 2; i++) // data.Length Limit to 4 chars
126+
{
127+
sBuilder.Append(data[i].ToString("x2"));
128+
}
129+
130+
// Return the hexadecimal string.
131+
return sBuilder.ToString();
132+
}
133+
134+
/// <summary>
135+
/// Creates a structured directory of nodes from the paths of an OpenAPI document.
136+
/// </summary>
137+
/// <param name="doc">The OpenAPI document.</param>
138+
/// <param name="label">Name tag for labelling the nodes in the directory structure.</param>
139+
/// <returns>The root node of the created directory structure.</returns>
140+
public static OpenApiUrlSpaceNode Create(OpenApiDocument doc, string label = "")
141+
{
142+
OpenApiUrlSpaceNode root = null;
143+
144+
var paths = doc?.Paths;
145+
if (paths != null)
146+
{
147+
root = new OpenApiUrlSpaceNode("");
148+
149+
foreach (var path in paths)
150+
{
151+
root.Attach(path.Key, path.Value, label);
152+
}
153+
}
154+
return root;
155+
}
156+
157+
/// <summary>
158+
/// Retrieves the paths from an OpenAPI document and appends the items to a node.
159+
/// </summary>
160+
/// <param name="doc">The OpenAPI document.</param>
161+
/// <param name="label">Name tag for labelling the nodes in the directory structure.</param>
162+
public void Attach(OpenApiDocument doc, string label)
163+
{
164+
var paths = doc?.Paths;
165+
if (paths != null)
166+
{
167+
foreach (var path in paths)
168+
{
169+
Attach(path.Key, path.Value, label);
170+
}
171+
}
172+
}
173+
174+
/// <summary>
175+
/// Appends an OpenAPI path and the PathItems to a node.
176+
/// </summary>
177+
/// <param name="path">An OpenAPI path.</param>
178+
/// <param name="pathItem">Path Item object that describes the operations available on an OpenAPI path.</param>
179+
/// <param name="label">A name tag for labelling the node.</param>
180+
/// <returns>A node describing an OpenAPI path.</returns>
181+
public OpenApiUrlSpaceNode Attach(string path, OpenApiPathItem pathItem, string label)
182+
{
183+
if (path.StartsWith("/"))
184+
{
185+
// Remove leading slash
186+
path = path.Substring(1);
187+
}
188+
var segments = path.Split('/');
189+
return Attach(segments, pathItem, label, "");
190+
}
191+
192+
/// <summary>
193+
/// Assembles the constituent properties of a node.
194+
/// </summary>
195+
/// <param name="segments">IEnumerable subdirectories of a relative path.</param>
196+
/// <param name="pathItem">Path Item object that describes the operations available on an OpenAPI path.</param>
197+
/// <param name="label">A name tag for labelling the node.</param>
198+
/// <param name="currentPath">The relative path of a node.</param>
199+
/// <returns>A node with all constituent properties assembled.</returns>
200+
private OpenApiUrlSpaceNode Attach(IEnumerable<string> segments, OpenApiPathItem pathItem, string label, string currentPath)
201+
{
202+
var segment = segments.FirstOrDefault();
203+
if (string.IsNullOrEmpty(segment))
204+
{
205+
if (PathItem == null)
206+
{
207+
PathItem = pathItem;
208+
Path = currentPath;
209+
Label = label;
210+
}
211+
return this;
212+
}
213+
214+
// If the child segment has already been defined, then insert into it
215+
if (Children.ContainsKey(segment))
216+
{
217+
return Children[segment].Attach(segments.Skip(1), pathItem, label, currentPath + "\\" + segment );
218+
}
219+
else
220+
{
221+
var node = new OpenApiUrlSpaceNode(segment)
222+
{
223+
Path = currentPath + "\\" + segment
224+
};
225+
226+
Children[segment] = node;
227+
return node.Attach(segments.Skip(1), pathItem, label, currentPath + "\\" + segment);
228+
}
229+
}
230+
}
231+
}

0 commit comments

Comments
 (0)