diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index c7bf1a558..c757f4031 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -254,7 +254,7 @@ private static async Task GetOpenApiAsync(HidiOptions options, else if (!string.IsNullOrEmpty(options.OpenApi)) { stream = await GetStreamAsync(options.OpenApi, logger, cancellationToken).ConfigureAwait(false); - var result = await ParseOpenApiAsync(options.OpenApi, format, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false); + var result = await ParseOpenApiAsync(options.OpenApi, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false); document = result.Document; } else throw new InvalidOperationException("No input file path or URL provided"); @@ -351,8 +351,7 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe try { using var stream = await GetStreamAsync(openApi, logger, cancellationToken).ConfigureAwait(false); - var openApiFormat = !string.IsNullOrEmpty(openApi) ? GetOpenApiFormat(openApi, logger) : OpenApiFormat.Yaml; - result = await ParseOpenApiAsync(openApi, openApiFormat.GetDisplayName(),false, logger, stream, cancellationToken).ConfigureAwait(false); + result = await ParseOpenApiAsync(openApi, false, logger, stream, cancellationToken).ConfigureAwait(false); using (logger.BeginScope("Calculating statistics")) { @@ -380,7 +379,7 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe return result.Diagnostic.Errors.Count == 0; } - private static async Task ParseOpenApiAsync(string openApiFile, string format, bool inlineExternal, ILogger logger, Stream stream, CancellationToken cancellationToken = default) + private static async Task ParseOpenApiAsync(string openApiFile, bool inlineExternal, ILogger logger, Stream stream, CancellationToken cancellationToken = default) { ReadResult result; var stopwatch = Stopwatch.StartNew(); @@ -396,7 +395,7 @@ private static async Task ParseOpenApiAsync(string openApiFile, stri new Uri("file://" + new FileInfo(openApiFile).DirectoryName + Path.DirectorySeparatorChar) }; - result = await OpenApiDocument.LoadAsync(stream, format, settings, cancellationToken).ConfigureAwait(false); + result = await OpenApiDocument.LoadAsync(stream, settings: settings, cancellationToken: cancellationToken).ConfigureAwait(false); logger.LogTrace("{Timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds); diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 16f5d6a01..3acbd05ab 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -160,7 +160,7 @@ public void SerializeAsV3(IOpenApiWriter writer) /// private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version, Action callback) { - Utils.CheckArgumentNull(writer);; + Utils.CheckArgumentNull(writer); writer.WriteStartObject(); diff --git a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs index 33bea8fb5..9424f053f 100644 --- a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs +++ b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs @@ -39,7 +39,11 @@ public static ReadResult Load(MemoryStream stream, string format = null, OpenApiReaderSettings settings = null) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(stream); +#else if (stream is null) throw new ArgumentNullException(nameof(stream)); +#endif settings ??= new OpenApiReaderSettings(); // Get the format of the stream if not provided @@ -112,7 +116,11 @@ public static async Task LoadAsync(string url, OpenApiSpecVersion version, /// public static async Task LoadAsync(Stream input, string format = null, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(input); +#else if (input is null) throw new ArgumentNullException(nameof(input)); +#endif settings ??= new OpenApiReaderSettings(); Stream preparedStream; @@ -160,7 +168,11 @@ public static async Task LoadAsync(Stream input, CancellationToken token = default) where T : IOpenApiElement { Utils.CheckArgumentNull(openApiDocument); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(input); +#else if (input is null) throw new ArgumentNullException(nameof(input)); +#endif if (input is MemoryStream memoryStream) { return Load(memoryStream, version, format, openApiDocument, out var _, settings); @@ -185,7 +197,11 @@ public static ReadResult Parse(string input, string format = null, OpenApiReaderSettings settings = null) { - if (input is null) throw new ArgumentNullException(nameof(input)); +#if NET6_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(input); +#else + if (string.IsNullOrEmpty(input)) throw new ArgumentNullException(nameof(input)); +#endif format ??= InspectInputFormat(input); settings ??= new OpenApiReaderSettings(); @@ -212,7 +228,11 @@ public static T Parse(string input, string format = null, OpenApiReaderSettings settings = null) where T : IOpenApiElement { - if (input is null) throw new ArgumentNullException(nameof(input)); +#if NET6_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(input); +#else + if (string.IsNullOrEmpty(input)) throw new ArgumentNullException(nameof(input)); +#endif format ??= InspectInputFormat(input); settings ??= new OpenApiReaderSettings(); using var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); @@ -278,11 +298,12 @@ private static ReadResult InternalLoad(MemoryStream input, string format, OpenAp var response = await _httpClient.GetAsync(url, token).ConfigureAwait(false); var mediaType = response.Content.Headers.ContentType.MediaType; var contentType = mediaType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0]; - format = contentType.Split('/').LastOrDefault(); + format = contentType.Split('/').Last().Split('+').Last().Split('-').Last(); + // for non-standard MIME types e.g. text/x-yaml used in older libs or apps #if NETSTANDARD2_0 stream = await response.Content.ReadAsStreamAsync(); #else - stream = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);; + stream = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false); #endif return (stream, format); } @@ -321,8 +342,12 @@ private static string InspectInputFormat(string input) private static string InspectStreamFormat(Stream stream) { - if (stream == null) throw new ArgumentNullException(nameof(stream)); - +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(stream); +#else + if (stream is null) throw new ArgumentNullException(nameof(stream)); +#endif + long initialPosition = stream.Position; int firstByte = stream.ReadByte(); diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index e61ee8fbc..ea287db5e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -26,6 +26,7 @@ namespace Microsoft.OpenApi.Readers.Tests.V3Tests public class OpenApiDocumentTests { private const string SampleFolderPath = "V3Tests/Samples/OpenApiDocument/"; + private const string codacyApi = "https://api.codacy.com/api/api-docs/swagger.yaml"; public OpenApiDocumentTests() { @@ -1362,5 +1363,13 @@ public async Task ParseDocumentWithExampleReferencesPasses() var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "docWithExampleReferences.yaml")); Assert.Empty(result.Diagnostic.Errors); } + + [Fact] + public async Task ParseDocumentWithNonStandardMIMETypePasses() + { + // Act & Assert: Ensure NotSupportedException is not thrown for non-standard MIME type: text/x-yaml + var result = await OpenApiDocument.LoadAsync(codacyApi); + Assert.NotNull(result.Document); + } } }