Skip to content

Commit acf5c54

Browse files
committed
Resolved merge conflicts with vnext
2 parents 13812b1 + 976b5cc commit acf5c54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2707
-335
lines changed

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 132 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
using System.Net.Http;
1111
using System.Security;
1212
using System.Text;
13-
using System.Text.Json;
1413
using System.Threading.Tasks;
14+
using System.Text.Json;
1515
using Microsoft.Extensions.Logging;
1616
using System.Xml.Linq;
1717
using Microsoft.OData.Edm.Csdl;
@@ -45,8 +45,8 @@ public static async Task<int> TransformOpenApiDocument(
4545
string? version,
4646
OpenApiFormat? format,
4747
LogLevel loglevel,
48-
bool inline,
49-
bool resolveexternal,
48+
bool inlineLocal,
49+
bool inlineExternal,
5050
string filterbyoperationids,
5151
string filterbytags,
5252
string filterbycollection,
@@ -111,8 +111,9 @@ CancellationToken cancellationToken
111111
stopwatch.Restart();
112112
var result = await new OpenApiStreamReader(new OpenApiReaderSettings
113113
{
114-
ReferenceResolution = resolveexternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
115-
RuleSet = ValidationRuleSet.GetDefaultRuleSet()
114+
RuleSet = ValidationRuleSet.GetDefaultRuleSet(),
115+
LoadExternalRefs = inlineExternal,
116+
BaseUrl = openapi.StartsWith("http") ? new Uri(openapi) : new Uri("file:" + new FileInfo(openapi).DirectoryName + "\\")
116117
}
117118
).ReadAsync(stream);
118119

@@ -189,7 +190,8 @@ CancellationToken cancellationToken
189190

190191
var settings = new OpenApiWriterSettings()
191192
{
192-
ReferenceInline = inline ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
193+
InlineLocalReferences = inlineLocal,
194+
InlineExternalReferences = inlineExternal
193195
};
194196

195197
IOpenApiWriter writer = openApiFormat switch
@@ -276,7 +278,7 @@ public static async Task<int> ValidateOpenApiDocument(
276278
RuleSet = ValidationRuleSet.GetDefaultRuleSet()
277279
}
278280
).ReadAsync(stream);
279-
281+
280282
logger.LogTrace("{timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds);
281283

282284
document = result.OpenApiDocument;
@@ -353,6 +355,73 @@ public static async Task<OpenApiDocument> ConvertCsdlToOpenApi(Stream csdl)
353355
return document;
354356
}
355357

358+
/// <summary>
359+
/// Fixes the references in the resulting OpenApiDocument.
360+
/// </summary>
361+
/// <param name="document"> The converted OpenApiDocument.</param>
362+
/// <returns> A valid OpenApiDocument instance.</returns>
363+
public static OpenApiDocument FixReferences(OpenApiDocument document)
364+
{
365+
// This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance.
366+
// So we write it out, and read it back in again to fix it up.
367+
368+
var sb = new StringBuilder();
369+
document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb)));
370+
var doc = new OpenApiStringReader().Read(sb.ToString(), out _);
371+
372+
return doc;
373+
}
374+
375+
private static async Task<Stream> GetStream(string input, ILogger logger)
376+
{
377+
var stopwatch = new Stopwatch();
378+
stopwatch.Start();
379+
380+
Stream stream;
381+
if (input.StartsWith("http"))
382+
{
383+
try
384+
{
385+
var httpClientHandler = new HttpClientHandler()
386+
{
387+
SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
388+
};
389+
using var httpClient = new HttpClient(httpClientHandler)
390+
{
391+
DefaultRequestVersion = HttpVersion.Version20
392+
};
393+
stream = await httpClient.GetStreamAsync(input);
394+
}
395+
catch (HttpRequestException ex)
396+
{
397+
logger.LogError($"Could not download the file at {input}, reason{ex}");
398+
return null;
399+
}
400+
}
401+
else
402+
{
403+
try
404+
{
405+
var fileInput = new FileInfo(input);
406+
stream = fileInput.OpenRead();
407+
}
408+
catch (Exception ex) when (ex is FileNotFoundException ||
409+
ex is PathTooLongException ||
410+
ex is DirectoryNotFoundException ||
411+
ex is IOException ||
412+
ex is UnauthorizedAccessException ||
413+
ex is SecurityException ||
414+
ex is NotSupportedException)
415+
{
416+
logger.LogError($"Could not open the file at {input}, reason: {ex.Message}");
417+
return null;
418+
}
419+
}
420+
stopwatch.Stop();
421+
logger.LogTrace("{timestamp}ms: Read file {input}", stopwatch.ElapsedMilliseconds, input);
422+
return stream;
423+
}
424+
356425
/// <summary>
357426
/// Takes in a file stream, parses the stream into a JsonDocument and gets a list of paths and Http methods
358427
/// </summary>
@@ -365,42 +434,76 @@ public static Dictionary<string, List<string>> ParseJsonCollectionFile(Stream st
365434
logger.LogTrace("Parsing the json collection file into a JsonDocument");
366435
using var document = JsonDocument.Parse(stream);
367436
var root = document.RootElement;
368-
var itemElement = root.GetProperty("item");
369-
foreach (var requestObject in itemElement.EnumerateArray().Select(item => item.GetProperty("request")))
370-
{
371-
// Fetch list of methods and urls from collection, store them in a dictionary
372-
var path = requestObject.GetProperty("url").GetProperty("raw").ToString();
373-
var method = requestObject.GetProperty("method").ToString();
374437

375-
if (!requestUrls.ContainsKey(path))
438+
requestUrls = EnumerateJsonDocument(root, requestUrls);
439+
logger.LogTrace("Finished fetching the list of paths and Http methods defined in the Postman collection.");
440+
441+
return requestUrls;
442+
}
443+
444+
private static Dictionary<string, List<string>> EnumerateJsonDocument(JsonElement itemElement, Dictionary<string, List<string>> paths)
445+
{
446+
var itemsArray = itemElement.GetProperty("item");
447+
448+
foreach (var item in itemsArray.EnumerateArray())
449+
{
450+
if(item.ValueKind == JsonValueKind.Object)
376451
{
377-
requestUrls.Add(path, new List<string> { method });
452+
if(item.TryGetProperty("request", out var request))
453+
{
454+
// Fetch list of methods and urls from collection, store them in a dictionary
455+
var path = request.GetProperty("url").GetProperty("raw").ToString();
456+
var method = request.GetProperty("method").ToString();
457+
if (!paths.ContainsKey(path))
458+
{
459+
paths.Add(path, new List<string> { method });
460+
}
461+
else
462+
{
463+
paths[path].Add(method);
464+
}
465+
}
466+
else
467+
{
468+
EnumerateJsonDocument(item, paths);
469+
}
378470
}
379471
else
380472
{
381-
requestUrls[path].Add(method);
473+
EnumerateJsonDocument(item, paths);
382474
}
383475
}
384-
logger.LogTrace("Finished fetching the list of paths and Http methods defined in the Postman collection.");
385-
return requestUrls;
476+
477+
return paths;
386478
}
387479

388480
/// <summary>
389481
/// Fixes the references in the resulting OpenApiDocument.
390482
/// </summary>
391483
/// <param name="document"> The converted OpenApiDocument.</param>
392484
/// <returns> A valid OpenApiDocument instance.</returns>
393-
private static OpenApiDocument FixReferences(OpenApiDocument document)
394-
{
395-
// This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance.
396-
// So we write it out, and read it back in again to fix it up.
397-
398-
var sb = new StringBuilder();
399-
document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb)));
400-
var doc = new OpenApiStringReader().Read(sb.ToString(), out _);
401-
402-
return doc;
403-
}
485+
// private static OpenApiDocument FixReferences2(OpenApiDocument document)
486+
// {
487+
// // This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance.
488+
// // So we write it out, and read it back in again to fix it up.
489+
490+
// OpenApiDocument document;
491+
// logger.LogTrace("Parsing the OpenApi file");
492+
// var result = await new OpenApiStreamReader(new OpenApiReaderSettings
493+
// {
494+
// RuleSet = ValidationRuleSet.GetDefaultRuleSet(),
495+
// BaseUrl = new Uri(openapi)
496+
// }
497+
// ).ReadAsync(stream);
498+
499+
// document = result.OpenApiDocument;
500+
// var context = result.OpenApiDiagnostic;
501+
// var sb = new StringBuilder();
502+
// document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb)));
503+
// var doc = new OpenApiStringReader().Read(sb.ToString(), out _);
504+
505+
// return doc;
506+
// }
404507

405508
/// <summary>
406509
/// Reads stream from file system or makes HTTP request depending on the input string

src/Microsoft.OpenApi.Hidi/Program.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ static async Task Main(string[] args)
5151
var filterByCollectionOption = new Option<string>("--filter-by-collection", "Filters OpenApiDocument by Postman collection provided");
5252
filterByCollectionOption.AddAlias("-c");
5353

54-
var inlineOption = new Option<bool>("--inline", "Inline $ref instances");
55-
inlineOption.AddAlias("-i");
54+
var inlineLocalOption = new Option<bool>("--inlineLocal", "Inline local $ref instances");
55+
inlineLocalOption.AddAlias("-il");
5656

57-
var resolveExternalOption = new Option<bool>("--resolve-external", "Resolve external $refs");
58-
resolveExternalOption.AddAlias("-ex");
57+
var inlineExternalOption = new Option<bool>("--inlineExternal", "Inline external $ref instances");
58+
inlineExternalOption.AddAlias("-ie");
5959

6060
var validateCommand = new Command("validate")
6161
{
@@ -78,12 +78,12 @@ static async Task Main(string[] args)
7878
filterByOperationIdsOption,
7979
filterByTagsOption,
8080
filterByCollectionOption,
81-
inlineOption,
82-
resolveExternalOption,
81+
inlineLocalOption,
82+
inlineExternalOption
8383
};
8484

8585
transformCommand.SetHandler<string, string, string, FileInfo, bool, string?, OpenApiFormat?, LogLevel, bool, bool, string, string, string, CancellationToken> (
86-
OpenApiService.TransformOpenApiDocument, descriptionOption, csdlOption, csdlFilterOption, outputOption, cleanOutputOption, versionOption, formatOption, logLevelOption, inlineOption, resolveExternalOption, filterByOperationIdsOption, filterByTagsOption, filterByCollectionOption);
86+
OpenApiService.TransformOpenApiDocument, descriptionOption, csdlOption, csdlFilterOption, outputOption, cleanOutputOption, versionOption, formatOption, logLevelOption, inlineLocalOption, inlineExternalOption, filterByOperationIdsOption, filterByTagsOption, filterByCollectionOption);
8787

8888
rootCommand.Add(transformCommand);
8989
rootCommand.Add(validateCommand);

src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,10 @@
44
using Microsoft.OpenApi.Any;
55
using Microsoft.OpenApi.Interfaces;
66
using Microsoft.OpenApi.Readers.Interface;
7-
using Microsoft.OpenApi.Readers.ParseNodes;
8-
using Microsoft.OpenApi.Readers.Services;
97
using Microsoft.OpenApi.Validations;
108
using System;
119
using System.Collections.Generic;
1210
using System.IO;
13-
using System.Linq;
14-
using System.Text;
15-
using System.Threading.Tasks;
1611

1712
namespace Microsoft.OpenApi.Readers
1813
{
@@ -30,7 +25,7 @@ public enum ReferenceResolutionSetting
3025
/// </summary>
3126
ResolveLocalReferences,
3227
/// <summary>
33-
/// Convert all references to references of valid domain objects.
28+
/// ResolveAllReferences effectively means load external references. Will be removed in v2. External references are never "resolved".
3429
/// </summary>
3530
ResolveAllReferences
3631
}
@@ -43,8 +38,14 @@ public class OpenApiReaderSettings
4338
/// <summary>
4439
/// Indicates how references in the source document should be handled.
4540
/// </summary>
41+
/// <remarks>This setting will be going away in the next major version of this library. Use GetEffective on model objects to get resolved references.</remarks>
4642
public ReferenceResolutionSetting ReferenceResolution { get; set; } = ReferenceResolutionSetting.ResolveLocalReferences;
4743

44+
/// <summary>
45+
/// When external references are found, load them into a shared workspace
46+
/// </summary>
47+
public bool LoadExternalRefs { get; set; } = false;
48+
4849
/// <summary>
4950
/// Dictionary of parsers for converting extensions into strongly typed classes
5051
/// </summary>

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

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

4+
using System;
45
using System.IO;
56
using System.Threading.Tasks;
67
using Microsoft.OpenApi.Interfaces;
@@ -23,6 +24,12 @@ public class OpenApiStreamReader : IOpenApiReader<Stream, OpenApiDiagnostic>
2324
public OpenApiStreamReader(OpenApiReaderSettings settings = null)
2425
{
2526
_settings = settings ?? new OpenApiReaderSettings();
27+
28+
if((_settings.ReferenceResolution == ReferenceResolutionSetting.ResolveAllReferences || _settings.LoadExternalRefs)
29+
&& _settings.BaseUrl == null)
30+
{
31+
throw new ArgumentException("BaseUrl must be provided to resolve external references.");
32+
}
2633
}
2734

2835
/// <summary>

0 commit comments

Comments
 (0)