Skip to content

Commit ce24a81

Browse files
authored
Merge pull request #697 from microsoft/mk/align-hidi-params-with-kiota
Align hidi params with kiota
2 parents 21925a3 + a5e86dc commit ce24a81

File tree

6 files changed

+235
-78
lines changed

6 files changed

+235
-78
lines changed

src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
14+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
16+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
1317
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21216.1" />
1418
</ItemGroup>
1519

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 167 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.IO;
78
using System.Linq;
89
using System.Net;
910
using System.Net.Http;
11+
using System.Security;
1012
using System.Text;
1113
using System.Text.Json;
14+
using System.Threading.Tasks;
15+
using Microsoft.Extensions.Logging;
1216
using Microsoft.OpenApi.Extensions;
1317
using Microsoft.OpenApi.Models;
1418
using Microsoft.OpenApi.Readers;
@@ -18,123 +22,201 @@
1822

1923
namespace Microsoft.OpenApi.Hidi
2024
{
21-
public static class OpenApiService
25+
public class OpenApiService
2226
{
23-
public static void ProcessOpenApiDocument(
24-
string input,
27+
public static async void ProcessOpenApiDocument(
28+
string openapi,
2529
FileInfo output,
2630
OpenApiSpecVersion? version,
2731
OpenApiFormat? format,
28-
string filterByOperationIds,
29-
string filterByTags,
30-
string filterByCollection,
32+
LogLevel loglevel,
33+
string filterbyoperationids,
34+
string filterbytags,
35+
string filterbycollection,
3136
bool inline,
32-
bool resolveExternal)
37+
bool resolveexternal)
3338
{
34-
if (string.IsNullOrEmpty(input))
39+
var logger = ConfigureLoggerInstance(loglevel);
40+
41+
try
3542
{
36-
throw new ArgumentNullException(nameof(input));
43+
if (string.IsNullOrEmpty(openapi))
44+
{
45+
throw new ArgumentNullException(nameof(openapi));
46+
}
3747
}
38-
if(output == null)
48+
catch (ArgumentNullException ex)
3949
{
40-
throw new ArgumentException(nameof(output));
50+
logger.LogError(ex.Message);
51+
return;
4152
}
42-
if (output.Exists)
53+
try
4354
{
44-
throw new IOException("The file you're writing to already exists. Please input a new output path.");
55+
if(output == null)
56+
{
57+
throw new ArgumentException(nameof(output));
58+
}
4559
}
60+
catch (ArgumentException ex)
61+
{
62+
logger.LogError(ex.Message);
63+
return;
64+
}
65+
try
66+
{
67+
if (output.Exists)
68+
{
69+
throw new IOException("The file you're writing to already exists. Please input a new file path.");
70+
}
71+
}
72+
catch (IOException ex)
73+
{
74+
logger.LogError(ex.Message);
75+
return;
76+
}
77+
78+
var stream = await GetStream(openapi, logger);
4679

47-
var stream = GetStream(input);
80+
// Parsing OpenAPI file
81+
var stopwatch = new Stopwatch();
82+
stopwatch.Start();
83+
logger.LogTrace("Parsing OpenApi file");
4884
var result = new OpenApiStreamReader(new OpenApiReaderSettings
4985
{
50-
ReferenceResolution = resolveExternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
86+
ReferenceResolution = resolveexternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
5187
RuleSet = ValidationRuleSet.GetDefaultRuleSet()
5288
}
5389
).ReadAsync(stream).GetAwaiter().GetResult();
54-
5590
var document = result.OpenApiDocument;
91+
stopwatch.Stop();
92+
93+
var context = result.OpenApiDiagnostic;
94+
if (context.Errors.Count > 0)
95+
{
96+
var errorReport = new StringBuilder();
97+
98+
foreach (var error in context.Errors)
99+
{
100+
errorReport.AppendLine(error.ToString());
101+
}
102+
logger.LogError($"{stopwatch.ElapsedMilliseconds}ms: OpenApi Parsing errors {string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())}");
103+
}
104+
else
105+
{
106+
logger.LogTrace("{timestamp}ms: Parsed OpenApi successfully. {count} paths found.", stopwatch.ElapsedMilliseconds, document.Paths.Count);
107+
}
108+
56109
Func<string, OperationType?, OpenApiOperation, bool> predicate;
57110

58-
// Check if filter options are provided, then execute
59-
if (!string.IsNullOrEmpty(filterByOperationIds) && !string.IsNullOrEmpty(filterByTags))
111+
// Check if filter options are provided, then slice the OpenAPI document
112+
if (!string.IsNullOrEmpty(filterbyoperationids) && !string.IsNullOrEmpty(filterbytags))
60113
{
61114
throw new InvalidOperationException("Cannot filter by operationIds and tags at the same time.");
62115
}
63-
if (!string.IsNullOrEmpty(filterByOperationIds))
116+
if (!string.IsNullOrEmpty(filterbyoperationids))
64117
{
65-
predicate = OpenApiFilterService.CreatePredicate(operationIds: filterByOperationIds);
118+
logger.LogTrace("Creating predicate based on the operationIds supplied.");
119+
predicate = OpenApiFilterService.CreatePredicate(operationIds: filterbyoperationids);
120+
121+
logger.LogTrace("Creating subset OpenApi document.");
66122
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
67123
}
68-
if (!string.IsNullOrEmpty(filterByTags))
124+
if (!string.IsNullOrEmpty(filterbytags))
69125
{
70-
predicate = OpenApiFilterService.CreatePredicate(tags: filterByTags);
71-
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
72-
}
126+
logger.LogTrace("Creating predicate based on the tags supplied.");
127+
predicate = OpenApiFilterService.CreatePredicate(tags: filterbytags);
73128

74-
if (!string.IsNullOrEmpty(filterByCollection))
75-
{
76-
var fileStream = GetStream(filterByCollection);
77-
var requestUrls = ParseJsonCollectionFile(fileStream);
78-
predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source:document);
129+
logger.LogTrace("Creating subset OpenApi document.");
79130
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
80131
}
81-
82-
var context = result.OpenApiDiagnostic;
83-
84-
if (context.Errors.Count > 0)
132+
if (!string.IsNullOrEmpty(filterbycollection))
85133
{
86-
var errorReport = new StringBuilder();
134+
var fileStream = await GetStream(filterbycollection, logger);
135+
var requestUrls = ParseJsonCollectionFile(fileStream, logger);
87136

88-
foreach (var error in context.Errors)
89-
{
90-
errorReport.AppendLine(error.ToString());
91-
}
137+
logger.LogTrace("Creating predicate based on the paths and Http methods defined in the Postman collection.");
138+
predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source:document);
92139

93-
throw new ArgumentException(string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray()));
140+
logger.LogTrace("Creating subset OpenApi document.");
141+
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
94142
}
95-
143+
144+
logger.LogTrace("Creating a new file");
96145
using var outputStream = output?.Create();
97-
98-
var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out;
146+
var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out;
99147

100148
var settings = new OpenApiWriterSettings()
101149
{
102150
ReferenceInline = inline ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
103151
};
104152

105-
var openApiFormat = format ?? GetOpenApiFormat(input);
153+
var openApiFormat = format ?? GetOpenApiFormat(openapi, logger);
106154
var openApiVersion = version ?? result.OpenApiDiagnostic.SpecificationVersion;
107155
IOpenApiWriter writer = openApiFormat switch
108156
{
109157
OpenApiFormat.Json => new OpenApiJsonWriter(textWriter, settings),
110158
OpenApiFormat.Yaml => new OpenApiYamlWriter(textWriter, settings),
111159
_ => throw new ArgumentException("Unknown format"),
112160
};
161+
162+
logger.LogTrace("Serializing to OpenApi document using the provided spec version and writer");
163+
164+
stopwatch.Start();
113165
document.Serialize(writer, openApiVersion);
166+
stopwatch.Stop();
167+
168+
logger.LogTrace($"Finished serializing in {stopwatch.ElapsedMilliseconds}ms");
114169

115170
textWriter.Flush();
116171
}
117172

118-
private static Stream GetStream(string input)
173+
private static async Task<Stream> GetStream(string input, ILogger logger)
119174
{
175+
var stopwatch = new Stopwatch();
176+
stopwatch.Start();
177+
120178
Stream stream;
121179
if (input.StartsWith("http"))
122180
{
123-
var httpClient = new HttpClient(new HttpClientHandler()
181+
try
124182
{
125-
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
126-
})
183+
using var httpClientHandler = new HttpClientHandler()
184+
{
185+
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
186+
};
187+
using var httpClient = new HttpClient(httpClientHandler)
188+
{
189+
DefaultRequestVersion = HttpVersion.Version20
190+
};
191+
stream = await httpClient.GetStreamAsync(input);
192+
}
193+
catch (HttpRequestException ex)
127194
{
128-
DefaultRequestVersion = HttpVersion.Version20
129-
};
130-
stream = httpClient.GetStreamAsync(input).Result;
195+
logger.LogError($"Could not download the file at {input}, reason{ex}");
196+
return null;
197+
}
131198
}
132199
else
133200
{
134-
var fileInput = new FileInfo(input);
135-
stream = fileInput.OpenRead();
201+
try
202+
{
203+
var fileInput = new FileInfo(input);
204+
stream = fileInput.OpenRead();
205+
}
206+
catch (Exception ex) when (ex is FileNotFoundException ||
207+
ex is PathTooLongException ||
208+
ex is DirectoryNotFoundException ||
209+
ex is IOException ||
210+
ex is UnauthorizedAccessException ||
211+
ex is SecurityException ||
212+
ex is NotSupportedException)
213+
{
214+
logger.LogError($"Could not open the file at {input}, reason: {ex.Message}");
215+
return null;
216+
}
136217
}
137-
218+
stopwatch.Stop();
219+
logger.LogTrace("{timestamp}ms: Read file {input}", stopwatch.ElapsedMilliseconds, input);
138220
return stream;
139221
}
140222

@@ -143,11 +225,11 @@ private static Stream GetStream(string input)
143225
/// </summary>
144226
/// <param name="stream"> A file stream.</param>
145227
/// <returns> A dictionary of request urls and http methods from a collection.</returns>
146-
public static Dictionary<string, List<string>> ParseJsonCollectionFile(Stream stream)
228+
public static Dictionary<string, List<string>> ParseJsonCollectionFile(Stream stream, ILogger logger)
147229
{
148230
var requestUrls = new Dictionary<string, List<string>>();
149231

150-
// Convert file to JsonDocument
232+
logger.LogTrace("Parsing the json collection file into a JsonDocument");
151233
using var document = JsonDocument.Parse(stream);
152234
var root = document.RootElement;
153235
var itemElement = root.GetProperty("item");
@@ -166,21 +248,21 @@ public static Dictionary<string, List<string>> ParseJsonCollectionFile(Stream st
166248
requestUrls[path].Add(method);
167249
}
168250
}
169-
251+
logger.LogTrace("Finished fetching the list of paths and Http methods defined in the Postman collection.");
170252
return requestUrls;
171253
}
172254

173-
internal static void ValidateOpenApiDocument(string input)
255+
internal static async void ValidateOpenApiDocument(string openapi, LogLevel loglevel)
174256
{
175-
if (input == null)
257+
if (string.IsNullOrEmpty(openapi))
176258
{
177-
throw new ArgumentNullException("input");
259+
throw new ArgumentNullException(nameof(openapi));
178260
}
179-
180-
var stream = GetStream(input);
261+
var logger = ConfigureLoggerInstance(loglevel);
262+
var stream = await GetStream(openapi, logger);
181263

182264
OpenApiDocument document;
183-
265+
logger.LogTrace("Parsing the OpenApi file");
184266
document = new OpenApiStreamReader(new OpenApiReaderSettings
185267
{
186268
RuleSet = ValidationRuleSet.GetDefaultRuleSet()
@@ -199,12 +281,33 @@ internal static void ValidateOpenApiDocument(string input)
199281
var walker = new OpenApiWalker(statsVisitor);
200282
walker.Walk(document);
201283

284+
logger.LogTrace("Finished walking through the OpenApi document. Generating a statistics report..");
202285
Console.WriteLine(statsVisitor.GetStatisticsReport());
203286
}
204287

205-
private static OpenApiFormat GetOpenApiFormat(string input)
288+
private static OpenApiFormat GetOpenApiFormat(string openapi, ILogger logger)
289+
{
290+
logger.LogTrace("Getting the OpenApi format");
291+
return !openapi.StartsWith("http") && Path.GetExtension(openapi) == ".json" ? OpenApiFormat.Json : OpenApiFormat.Yaml;
292+
}
293+
294+
private static ILogger ConfigureLoggerInstance(LogLevel loglevel)
206295
{
207-
return !input.StartsWith("http") && Path.GetExtension(input) == ".json" ? OpenApiFormat.Json : OpenApiFormat.Yaml;
296+
// Configure logger options
297+
#if DEBUG
298+
loglevel = loglevel > LogLevel.Debug ? LogLevel.Debug : loglevel;
299+
#endif
300+
301+
var logger = LoggerFactory.Create((builder) => {
302+
builder
303+
.AddConsole()
304+
#if DEBUG
305+
.AddDebug()
306+
#endif
307+
.SetMinimumLevel(loglevel);
308+
}).CreateLogger<OpenApiService>();
309+
310+
return logger;
208311
}
209312
}
210313
}

0 commit comments

Comments
 (0)