Skip to content

Commit 4eff4c4

Browse files
committed
Move languageFormat to settings file
1 parent 9985902 commit 4eff4c4

File tree

9 files changed

+207
-60
lines changed

9 files changed

+207
-60
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.OpenApi.Any;
2+
using Microsoft.OpenApi.Interfaces;
3+
using System.Collections.Generic;
4+
5+
namespace Microsoft.OpenApi.Hidi.Extensions
6+
{
7+
internal static class OpenApiExtensibleExtensions
8+
{
9+
/// <summary>
10+
/// Gets an extension value from the extensions dictionary.
11+
/// </summary>
12+
/// <param name="extensions">A dictionary of <see cref="IOpenApiExtension"/>.</param>
13+
/// <param name="extensionKey">The key corresponding to the <see cref="IOpenApiExtension"/>.</param>
14+
/// <returns>A <see cref="string"/> value matching the provided extensionKey. Return null when extensionKey is not found. </returns>
15+
public static string GetExtension(this IDictionary<string, IOpenApiExtension> extensions, string extensionKey)
16+
{
17+
string extensionValue = null;
18+
if (extensions.TryGetValue(extensionKey, out var value) && value != null)
19+
{
20+
extensionValue = (value as OpenApiString)?.Value;
21+
}
22+
return extensionValue;
23+
}
24+
}
25+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Microsoft.OpenApi.Hidi.Extensions
6+
{
7+
/// <summary>
8+
/// Extension class for <see cref="string"/>.
9+
/// </summary>
10+
internal static class StringExtensions
11+
{
12+
/// <summary>
13+
/// Checks if the specified searchValue is equal to the target string based on the specified <see cref="StringComparison"/>.
14+
/// </summary>
15+
/// <param name="target">The target string to commpare to.</param>
16+
/// <param name="searchValue">The search string to seek.</param>
17+
/// <param name="comparison">The <see cref="StringComparison"/> to use. This defaults to <see cref="StringComparison.OrdinalIgnoreCase"/>.</param>
18+
/// <returns>true if the searchValue parameter occurs within this string; otherwise, false.</returns>
19+
public static bool IsEquals(this string target, string searchValue, StringComparison comparison = StringComparison.OrdinalIgnoreCase)
20+
{
21+
if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(searchValue))
22+
{
23+
return false;
24+
}
25+
return target.Equals(searchValue, comparison);
26+
}
27+
28+
/// <summary>
29+
/// Splits the target string in substrings based on the specified char separator.
30+
/// </summary>
31+
/// <param name="target">The target string to split by char. </param>
32+
/// <param name="separator">The char separator.</param>
33+
/// <returns>An <see cref="IList{String}"/> containing substrings.</returns>
34+
public static IList<string> SplitByChar(this string target, char separator)
35+
{
36+
if (string.IsNullOrWhiteSpace(target))
37+
{
38+
return new List<string>();
39+
}
40+
return target.Split(new char[] { separator }, StringSplitOptions.RemoveEmptyEntries).ToList();
41+
}
42+
}
43+
}

src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs

Lines changed: 97 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text;
45
using System.Text.RegularExpressions;
56
using Humanizer;
67
using Humanizer.Inflections;
8+
using Microsoft.OpenApi.Hidi.Extensions;
79
using Microsoft.OpenApi.Models;
810
using Microsoft.OpenApi.Services;
911

@@ -29,7 +31,7 @@ static PowerShellFormatter()
2931
Vocabularies.Default.AddSingular("(statistics)$", "$1");
3032
}
3133

32-
//TODO: FHL for PS
34+
//TODO: FHL taks for PS
3335
// Fixes (Order matters):
3436
// 1. Singularize operationId operationIdSegments.
3537
// 2. Add '_' to verb in an operationId.
@@ -50,9 +52,9 @@ public override void Visit(OpenApiSchema schema)
5052

5153
public override void Visit(OpenApiPathItem pathItem)
5254
{
53-
if (pathItem.Operations.ContainsKey(OperationType.Put))
55+
if (pathItem.Operations.TryGetValue(OperationType.Put, out var value))
5456
{
55-
var operationId = pathItem.Operations[OperationType.Put].OperationId;
57+
var operationId = value.OperationId;
5658
pathItem.Operations[OperationType.Put].OperationId = ResolvePutOperationId(operationId);
5759
}
5860

@@ -61,55 +63,38 @@ public override void Visit(OpenApiPathItem pathItem)
6163

6264
public override void Visit(OpenApiOperation operation)
6365
{
64-
if (operation.OperationId == null)
66+
if (string.IsNullOrWhiteSpace(operation.OperationId))
6567
throw new ArgumentNullException(nameof(operation.OperationId), $"OperationId is required {PathString}");
6668

6769
var operationId = operation.OperationId;
70+
var operationTypeExtension = operation.Extensions.GetExtension("x-ms-docs-operation-type");
71+
if (operationTypeExtension.IsEquals("function"))
72+
operation.Parameters = ResolveFunctionParameters(operation.Parameters);
6873

74+
// Order matters. Resolve operationId.
6975
operationId = RemoveHashSuffix(operationId);
76+
if (operationTypeExtension.IsEquals("action") || operationTypeExtension.IsEquals("function"))
77+
operationId = RemoveKeyTypeSegment(operationId, operation.Parameters);
78+
operationId = SingularizeAndDeduplicateOperationId(operationId.SplitByChar('.'));
7079
operationId = ResolveODataCastOperationId(operationId);
7180
operationId = ResolveByRefOperationId(operationId);
72-
73-
74-
var operationIdSegments = operationId.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).ToList();
75-
operationId = SingularizeAndDeduplicateOperationId(operationIdSegments);
81+
// Verb segment resolution should always be last. user.get -> user_Get
82+
operationId = ResolveVerbSegmentInOpertationId(operationId);
7683

7784
operation.OperationId = operationId;
7885
base.Visit(operation);
7986
}
8087

81-
private void AddAddtionalPropertiesToSchema(OpenApiSchema schema)
88+
private static string ResolveVerbSegmentInOpertationId(string operationId)
8289
{
83-
if (schema != null && !_schemaLoop.Contains(schema) && "object".Equals(schema?.Type, StringComparison.OrdinalIgnoreCase))
84-
{
85-
schema.AdditionalProperties = new OpenApiSchema() { Type = "object" };
86-
87-
/* Because 'additionalProperties' are now being walked,
88-
* we need a way to keep track of visited schemas to avoid
89-
* endlessly creating and walking them in an infinite recursion.
90-
*/
91-
_schemaLoop.Push(schema.AdditionalProperties);
92-
}
93-
}
94-
95-
private static void ResolveOneOfSchema(OpenApiSchema schema)
96-
{
97-
if (schema.OneOf?.Any() ?? false)
98-
{
99-
var newSchema = schema.OneOf.FirstOrDefault();
100-
schema.OneOf = null;
101-
FlattenSchema(schema, newSchema);
102-
}
103-
}
104-
105-
private static void ResolveAnyOfSchema(OpenApiSchema schema)
106-
{
107-
if (schema.AnyOf?.Any() ?? false)
108-
{
109-
var newSchema = schema.AnyOf.FirstOrDefault();
110-
schema.AnyOf = null;
111-
FlattenSchema(schema, newSchema);
112-
}
90+
var charPos = operationId.LastIndexOf('.', operationId.Length - 1);
91+
if (operationId.Contains('_') || charPos < 0)
92+
return operationId;
93+
// TODO: Optimize this call.
94+
var newOperationId = new StringBuilder(operationId);
95+
newOperationId[charPos] = '_';
96+
operationId = newOperationId.ToString();
97+
return operationId;
11398
}
11499

115100
private static string ResolvePutOperationId(string operationId)
@@ -158,6 +143,79 @@ private static string RemoveHashSuffix(string operationId)
158143
return s_hashSuffixRegex.Match(operationId).Value;
159144
}
160145

146+
private static string RemoveKeyTypeSegment(string operationId, IList<OpenApiParameter> parameters)
147+
{
148+
var segments = operationId.SplitByChar('.');
149+
foreach (var parameter in parameters)
150+
{
151+
var keyTypeExtension = parameter.Extensions.GetExtension("x-ms-docs-key-type");
152+
if (keyTypeExtension != null)
153+
{
154+
if (operationId.Contains(keyTypeExtension))
155+
{
156+
segments.Remove(keyTypeExtension);
157+
}
158+
}
159+
}
160+
return string.Join(".", segments);
161+
}
162+
163+
private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiParameter> parameters)
164+
{
165+
foreach (var parameter in parameters)
166+
{
167+
if (parameter.Content?.Any() ?? false)
168+
{
169+
// Replace content with a schema object of type array
170+
// for structured or collection-valued function parameters
171+
parameter.Content = null;
172+
parameter.Schema = new OpenApiSchema
173+
{
174+
Type = "array",
175+
Items = new OpenApiSchema
176+
{
177+
Type = "string"
178+
}
179+
};
180+
}
181+
}
182+
return parameters;
183+
}
184+
185+
private void AddAddtionalPropertiesToSchema(OpenApiSchema schema)
186+
{
187+
if (schema != null && !_schemaLoop.Contains(schema) && "object".Equals(schema?.Type, StringComparison.OrdinalIgnoreCase))
188+
{
189+
schema.AdditionalProperties = new OpenApiSchema() { Type = "object" };
190+
191+
/* Because 'additionalProperties' are now being walked,
192+
* we need a way to keep track of visited schemas to avoid
193+
* endlessly creating and walking them in an infinite recursion.
194+
*/
195+
_schemaLoop.Push(schema.AdditionalProperties);
196+
}
197+
}
198+
199+
private static void ResolveOneOfSchema(OpenApiSchema schema)
200+
{
201+
if (schema.OneOf?.Any() ?? false)
202+
{
203+
var newSchema = schema.OneOf.FirstOrDefault();
204+
schema.OneOf = null;
205+
FlattenSchema(schema, newSchema);
206+
}
207+
}
208+
209+
private static void ResolveAnyOfSchema(OpenApiSchema schema)
210+
{
211+
if (schema.AnyOf?.Any() ?? false)
212+
{
213+
var newSchema = schema.AnyOf.FirstOrDefault();
214+
schema.AnyOf = null;
215+
FlattenSchema(schema, newSchema);
216+
}
217+
}
218+
161219
private static void FlattenSchema(OpenApiSchema schema, OpenApiSchema newSchema)
162220
{
163221
if (newSchema != null)

src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ internal class TransformCommandHandler : ICommandHandler
2929
public Option<string> FilterByCollectionOption { get; set; }
3030
public Option<bool> InlineLocalOption { get; set; }
3131
public Option<bool> InlineExternalOption { get; set; }
32-
public Option<string?> LanguageFormatOption { get; set; }
3332

3433
public int Invoke(InvocationContext context)
3534
{
@@ -50,7 +49,6 @@ public async Task<int> InvokeAsync(InvocationContext context)
5049
LogLevel logLevel = context.ParseResult.GetValueForOption(LogLevelOption);
5150
bool inlineLocal = context.ParseResult.GetValueForOption(InlineLocalOption);
5251
bool inlineExternal = context.ParseResult.GetValueForOption(InlineExternalOption);
53-
string? languageFormatOption = context.ParseResult.GetValueForOption(LanguageFormatOption);
5452
string filterbyoperationids = context.ParseResult.GetValueForOption(FilterByOperationIdsOption);
5553
string filterbytags = context.ParseResult.GetValueForOption(FilterByTagsOption);
5654
string filterbycollection = context.ParseResult.GetValueForOption(FilterByCollectionOption);
@@ -61,7 +59,7 @@ public async Task<int> InvokeAsync(InvocationContext context)
6159
var logger = loggerFactory.CreateLogger<OpenApiService>();
6260
try
6361
{
64-
await OpenApiService.TransformOpenApiDocument(openapi, csdl, csdlFilter, output, cleanOutput, version, metadataVersion, format, terseOutput, settingsFile, inlineLocal, inlineExternal, languageFormatOption, filterbyoperationids, filterbytags, filterbycollection, logger, cancellationToken);
62+
await OpenApiService.TransformOpenApiDocument(openapi, csdl, csdlFilter, output, cleanOutput, version, metadataVersion, format, terseOutput, settingsFile, inlineLocal, inlineExternal, filterbyoperationids, filterbytags, filterbycollection, logger, cancellationToken);
6563

6664
return 0;
6765
}

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ public static async Task TransformOpenApiDocument(
4848
string settingsFile,
4949
bool inlineLocal,
5050
bool inlineExternal,
51-
string? languageFormatOption,
5251
string filterbyoperationids,
5352
string filterbytags,
5453
string filterbycollection,
@@ -84,7 +83,9 @@ CancellationToken cancellationToken
8483

8584
OpenApiDocument document = await GetOpenApi(openapi, csdl, csdlFilter, settingsFile, inlineExternal, logger, cancellationToken, metadataVersion);
8685
document = await FilterOpenApiDocument(filterbyoperationids, filterbytags, filterbycollection, document, logger, cancellationToken);
87-
if (!string.IsNullOrWhiteSpace(languageFormatOption) && languageFormatOption.Equals("PowerShell", StringComparison.InvariantCultureIgnoreCase))
86+
87+
var languageFormat = GetConfiguration(settingsFile).GetSection("LanguageFormat").Value;
88+
if (Extensions.StringExtensions.IsEquals(languageFormat, "PowerShell"))
8889
{
8990
// PowerShell Walker.
9091
var powerShellFormatter = new PowerShellFormatter();

src/Microsoft.OpenApi.Hidi/Program.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ internal static RootCommand CreateRootCommand()
7373
var inlineExternalOption = new Option<bool>("--inline-external", "Inline external $ref instances");
7474
inlineExternalOption.AddAlias("--ie");
7575

76-
// TODO: Move to settings file (--settings-path).
77-
var languageFormatOption = new Option<string>("--language-style", "Language to format the OpenAPI document. e.g. powershell");
78-
7976
var validateCommand = new Command("validate")
8077
{
8178
descriptionOption,
@@ -105,8 +102,7 @@ internal static RootCommand CreateRootCommand()
105102
filterByTagsOption,
106103
filterByCollectionOption,
107104
inlineLocalOption,
108-
inlineExternalOption,
109-
languageFormatOption
105+
inlineExternalOption
110106
};
111107

112108
transformCommand.Handler = new TransformCommandHandler
@@ -126,8 +122,7 @@ internal static RootCommand CreateRootCommand()
126122
FilterByTagsOption = filterByTagsOption,
127123
FilterByCollectionOption = filterByCollectionOption,
128124
InlineLocalOption = inlineLocalOption,
129-
InlineExternalOption = inlineExternalOption,
130-
LanguageFormatOption = languageFormatOption
125+
InlineExternalOption = inlineExternalOption
131126
};
132127

133128
var showCommand = new Command("show")

src/Microsoft.OpenApi/Services/CopyReferences.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ public override void Visit(IOpenApiReferenceable referenceable)
5252
}
5353
break;
5454

55+
case OpenApiRequestBody requestBody:
56+
EnsureComponentsExists();
57+
EnsureResponsesExists();
58+
EnsurRequestBodiesExists();
59+
if (!Components.RequestBodies.ContainsKey(requestBody.Reference.Id))
60+
{
61+
Components.RequestBodies.Add(requestBody.Reference.Id, requestBody);
62+
}
63+
break;
64+
5565
default:
5666
break;
5767
}
@@ -108,5 +118,13 @@ private void EnsureResponsesExists()
108118
_target.Components.Responses = new Dictionary<string, OpenApiResponse>();
109119
}
110120
}
121+
122+
private void EnsurRequestBodiesExists()
123+
{
124+
if (_target.Components.RequestBodies == null)
125+
{
126+
_target.Components.RequestBodies = new Dictionary<string, OpenApiRequestBody>();
127+
}
128+
}
111129
}
112130
}

0 commit comments

Comments
 (0)