diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 50407cd50..f381a4303 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -57,10 +57,10 @@ extends: version: 8.x # Install the nuget tool. - - task: NuGetToolInstaller@0 - displayName: 'Use NuGet >=5.2.0' + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet >=6.11.0' inputs: - versionSpec: '>=5.2.0' + versionSpec: '>=6.11.0' checkLatest: true # Build the Product project @@ -78,16 +78,17 @@ extends: projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.sln' arguments: '--configuration $(BuildConfiguration) --no-build' - - task: EsrpCodeSigning@2 - displayName: 'ESRP CodeSigning' + - task: EsrpCodeSigning@5 + displayName: 'ESRP CodeSigning binaries' inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' - FolderPath: src - signConfigType: inlineSignParams - UseMinimatch: true - Pattern: | - **\*.exe - **\*.dll + ConnectedServiceName: 'Federated DevX ESRP Managed Identity Connection' + AppRegistrationClientId: '65035b7f-7357-4f29-bf25-c5ee5c3949f8' + AppRegistrationTenantId: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2' + AuthAKVName: 'akv-prod-eastus' + AuthCertName: 'ReferenceLibraryPrivateCert' + AuthSignCertName: 'ReferencePackagePublisherCertificate' + FolderPath: '$(Build.SourcesDirectory)\src' + signConfigType: 'inlineSignParams' inlineOperation: | [ { @@ -126,7 +127,10 @@ extends: "toolVersion": "1.0" } ] - SessionTimeout: 20 + SessionTimeout: '20' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + PendingAnalysisWaitTimeoutMinutes: '5' # Pack core lib - pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg @@ -140,32 +144,39 @@ extends: - pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg displayName: 'pack Hidi' - - task: EsrpCodeSigning@2 + - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning Nuget Packages' inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' + ConnectedServiceName: 'Federated DevX ESRP Managed Identity Connection' + AppRegistrationClientId: '65035b7f-7357-4f29-bf25-c5ee5c3949f8' + AppRegistrationTenantId: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2' + AuthAKVName: 'akv-prod-eastus' + AuthCertName: 'ReferenceLibraryPrivateCert' + AuthSignCertName: 'ReferencePackagePublisherCertificate' FolderPath: '$(Build.ArtifactStagingDirectory)' Pattern: '*.nupkg' - signConfigType: inlineSignParams - UseMinimatch: true + signConfigType: 'inlineSignParams' inlineOperation: | [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetVerify", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: '60' + MaxConcurrency: '50' + MaxRetryAttempts: '5' + PendingAnalysisWaitTimeoutMinutes: '5' - task: PowerShell@2 displayName: "Get Hidi's version-number from .csproj" diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 000000000..2425160a4 --- /dev/null +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -0,0 +1,15 @@ +## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. + +pipelines: + 107: + usedNonDefaultBranch: true + retail: + source: + credscan: + lastModifiedDate: 2024-09-13 + eslint: + lastModifiedDate: 2024-09-13 + psscriptanalyzer: + lastModifiedDate: 2024-09-13 + armory: + lastModifiedDate: 2024-09-13 diff --git a/.editorconfig b/.editorconfig index 5b8c4b64e..e8f790dff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -121,3 +121,14 @@ csharp_preserve_single_line_blocks = true [*.vb] # Modifier preferences visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + + +# Verify settings +[*.{received,verified}.{txt,xml,json}] +charset = "utf-8-bom" +end_of_line = lf +indent_size = unset +indent_style = unset +insert_final_newline = false +tab_width = unset +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index bdb0cabc8..dea329be9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,6 @@ *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain + +# VerifyTests +*.verified.txt text eol=lf working-tree-encoding=UTF-8 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6b1c21db4..a61cbd408 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @irvinesunday @darrelmiller @zengin @coseguera @millicentachieng @MaggieKimani1 @andrueastman +* @irvinesunday @darrelmiller @gavinbarron @millicentachieng @MaggieKimani1 @andrueastman diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 790809712..ee951983c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -17,7 +17,7 @@ jobs: - name: Check out the repo uses: actions/checkout@v4 - name: Login to GitHub package feed - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} @@ -30,13 +30,13 @@ jobs: id: getversion - name: Push to GitHub Packages - Nightly if: ${{ github.ref == 'refs/heads/vnext' }} - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.7.0 with: push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly - name: Push to GitHub Packages - Release if: ${{ github.ref == 'refs/heads/master' }} - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.7.0 with: push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }} diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index bb3c028e7..a39756a42 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -24,12 +24,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E546B92F-20A EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2DE4-4900-ADB9-ABC37052040A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Hidi.Tests", "test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj", "{D8F799DD-04AC-4A13-B344-45A5B944450A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Trimming.Tests", "test\Microsoft.OpenApi.Trimming.Tests\Microsoft.OpenApi.Trimming.Tests.csproj", "{1D2E0C6E-B103-4CB6-912E-D56FA1501296}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,10 +56,6 @@ Global {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Debug|Any CPU.Build.0 = Debug|Any CPU {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.ActiveCfg = Release|Any CPU {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.Build.0 = Release|Any CPU - {AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AD79B61D-88CF-497C-9ED5-41AE3867C5AC}.Release|Any CPU.Build.0 = Release|Any CPU {254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Debug|Any CPU.Build.0 = Debug|Any CPU {254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -68,6 +64,10 @@ Global {D8F799DD-04AC-4A13-B344-45A5B944450A}.Debug|Any CPU.Build.0 = Debug|Any CPU {D8F799DD-04AC-4A13-B344-45A5B944450A}.Release|Any CPU.ActiveCfg = Release|Any CPU {D8F799DD-04AC-4A13-B344-45A5B944450A}.Release|Any CPU.Build.0 = Release|Any CPU + {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -78,9 +78,9 @@ Global {79933258-0126-4382-8755-D50820ECC483} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1} {AD83F991-DBF3-4251-8613-9CC54C826964} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A} {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A} - {AD79B61D-88CF-497C-9ED5-41AE3867C5AC} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A} {254841B5-7DAC-4D1D-A9C5-44FE5CE467BE} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1} {D8F799DD-04AC-4A13-B344-45A5B944450A} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A} + {1D2E0C6E-B103-4CB6-912E-D56FA1501296} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9F171EFC-0DB5-4B10-ABFA-AF48D52CC565} diff --git a/README.md b/README.md index 021e570f5..c804787c1 100644 --- a/README.md +++ b/README.md @@ -113,12 +113,6 @@ In order to test the validity of an OpenApi document, we avail the following too 5. Copy and paste your OpenAPI descriptions in the **Input Content** window or paste the path to the descriptions file in the **Input File** textbox and click on `Convert` to render the results. -# Build Status - -|**master**| -|--| -|[![Build status](https://ci.appveyor.com/api/projects/status/9l6hly3vjeu0tmtx/branch/master?svg=true)](https://ci.appveyor.com/project/MicrosoftOpenAPINETAdmin/openapi-net-54e7i/branch/master)| - # Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/AsyncCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/AsyncCommandHandler.cs new file mode 100644 index 000000000..385c00931 --- /dev/null +++ b/src/Microsoft.OpenApi.Hidi/Handlers/AsyncCommandHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.CommandLine.Invocation; +using System.Threading.Tasks; + +namespace Microsoft.OpenApi.Hidi.Handlers; + +internal abstract class AsyncCommandHandler : ICommandHandler +{ + public int Invoke(InvocationContext context) + { + throw new InvalidOperationException("This method should not be called"); + } + public abstract Task InvokeAsync(InvocationContext context); +} diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/PluginCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/PluginCommandHandler.cs index bd240f00e..b8f1155c7 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/PluginCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/PluginCommandHandler.cs @@ -11,18 +11,14 @@ namespace Microsoft.OpenApi.Hidi.Handlers { - internal class PluginCommandHandler : ICommandHandler + internal class PluginCommandHandler : AsyncCommandHandler { public CommandOptions CommandOptions { get; } public PluginCommandHandler(CommandOptions commandOptions) { CommandOptions = commandOptions; } - public int Invoke(InvocationContext context) - { - return InvokeAsync(context).GetAwaiter().GetResult(); - } - public async Task InvokeAsync(InvocationContext context) + public override async Task InvokeAsync(InvocationContext context) { var hidiOptions = new HidiOptions(context.ParseResult, CommandOptions); var cancellationToken = (CancellationToken)context.BindingContext.GetRequiredService(typeof(CancellationToken)); @@ -31,7 +27,7 @@ public async Task InvokeAsync(InvocationContext context) var logger = loggerFactory.CreateLogger(); try { - await OpenApiService.PluginManifest(hidiOptions, logger, cancellationToken).ConfigureAwait(false); + await OpenApiService.PluginManifestAsync(hidiOptions, logger, cancellationToken).ConfigureAwait(false); return 0; } diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/ShowCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/ShowCommandHandler.cs index dc2f6d8c8..e4f86c6f5 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/ShowCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/ShowCommandHandler.cs @@ -11,18 +11,14 @@ namespace Microsoft.OpenApi.Hidi.Handlers { - internal class ShowCommandHandler : ICommandHandler + internal class ShowCommandHandler : AsyncCommandHandler { public CommandOptions CommandOptions { get; set; } public ShowCommandHandler(CommandOptions commandOptions) { CommandOptions = commandOptions; } - public int Invoke(InvocationContext context) - { - return InvokeAsync(context).GetAwaiter().GetResult(); - } - public async Task InvokeAsync(InvocationContext context) + public override async Task InvokeAsync(InvocationContext context) { var hidiOptions = new HidiOptions(context.ParseResult, CommandOptions); var cancellationToken = (CancellationToken)context.BindingContext.GetRequiredService(typeof(CancellationToken)); @@ -31,7 +27,7 @@ public async Task InvokeAsync(InvocationContext context) var logger = loggerFactory.CreateLogger(); try { - await OpenApiService.ShowOpenApiDocument(hidiOptions, logger, cancellationToken).ConfigureAwait(false); + await OpenApiService.ShowOpenApiDocumentAsync(hidiOptions, logger, cancellationToken).ConfigureAwait(false); return 0; } diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs index c9f46b7ee..3a9a6322c 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs @@ -11,18 +11,14 @@ namespace Microsoft.OpenApi.Hidi.Handlers { - internal class TransformCommandHandler : ICommandHandler + internal class TransformCommandHandler : AsyncCommandHandler { public CommandOptions CommandOptions { get; } public TransformCommandHandler(CommandOptions commandOptions) { CommandOptions = commandOptions; } - public int Invoke(InvocationContext context) - { - return InvokeAsync(context).GetAwaiter().GetResult(); - } - public async Task InvokeAsync(InvocationContext context) + public override async Task InvokeAsync(InvocationContext context) { var hidiOptions = new HidiOptions(context.ParseResult, CommandOptions); var cancellationToken = (CancellationToken)context.BindingContext.GetRequiredService(typeof(CancellationToken)); @@ -31,7 +27,7 @@ public async Task InvokeAsync(InvocationContext context) var logger = loggerFactory.CreateLogger(); try { - await OpenApiService.TransformOpenApiDocument(hidiOptions, logger, cancellationToken).ConfigureAwait(false); + await OpenApiService.TransformOpenApiDocumentAsync(hidiOptions, logger, cancellationToken).ConfigureAwait(false); return 0; } diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/ValidateCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/ValidateCommandHandler.cs index 4c14cbef6..b2d4a4653 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/ValidateCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/ValidateCommandHandler.cs @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi.Hidi.Handlers { - internal class ValidateCommandHandler : ICommandHandler + internal class ValidateCommandHandler : AsyncCommandHandler { public CommandOptions CommandOptions { get; } @@ -19,12 +19,7 @@ public ValidateCommandHandler(CommandOptions commandOptions) { CommandOptions = commandOptions; } - - public int Invoke(InvocationContext context) - { - return InvokeAsync(context).GetAwaiter().GetResult(); - } - public async Task InvokeAsync(InvocationContext context) + public override async Task InvokeAsync(InvocationContext context) { var hidiOptions = new HidiOptions(context.ParseResult, CommandOptions); var cancellationToken = (CancellationToken)context.BindingContext.GetRequiredService(typeof(CancellationToken)); @@ -33,7 +28,7 @@ public async Task InvokeAsync(InvocationContext context) try { if (hidiOptions.OpenApi is null) throw new InvalidOperationException("OpenApi file is required"); - var isValid = await OpenApiService.ValidateOpenApiDocument(hidiOptions.OpenApi, logger, cancellationToken).ConfigureAwait(false); + var isValid = await OpenApiService.ValidateOpenApiDocumentAsync(hidiOptions.OpenApi, logger, cancellationToken).ConfigureAwait(false); return isValid is not false ? 0 : -1; } #if RELEASE diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 57b218388..a9a464704 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -9,7 +9,7 @@ enable hidi ./../../artifacts - 1.4.6 + 1.4.10 OpenAPI.NET CLI tool for slicing OpenAPI documents true @@ -33,11 +33,19 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + - - + + + + diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index 7cde3f2fb..c981639e9 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -48,7 +48,7 @@ static OpenApiService() /// /// Implementation of the transform command /// - public static async Task TransformOpenApiDocument(HidiOptions options, ILogger logger, CancellationToken cancellationToken = default) + public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILogger logger, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(options.OpenApi) && string.IsNullOrEmpty(options.Csdl) && string.IsNullOrEmpty(options.FilterOptions?.FilterByApiManifest)) { @@ -82,7 +82,7 @@ public static async Task TransformOpenApiDocument(HidiOptions options, ILogger l var openApiVersion = options.Version != null ? TryParseOpenApiSpecVersion(options.Version) : OpenApiSpecVersion.OpenApi3_1; // If ApiManifest is provided, set the referenced OpenAPI document - var apiDependency = await FindApiDependency(options.FilterOptions.FilterByApiManifest, logger, cancellationToken).ConfigureAwait(false); + var apiDependency = await FindApiDependencyAsync(options.FilterOptions.FilterByApiManifest, logger, cancellationToken).ConfigureAwait(false); if (apiDependency != null) { options.OpenApi = apiDependency.ApiDescripionUrl; @@ -92,13 +92,13 @@ public static async Task TransformOpenApiDocument(HidiOptions options, ILogger l JsonDocument? postmanCollection = null; if (!string.IsNullOrEmpty(options.FilterOptions?.FilterByCollection)) { - using var collectionStream = await GetStream(options.FilterOptions.FilterByCollection, logger, cancellationToken).ConfigureAwait(false); + using var collectionStream = await GetStreamAsync(options.FilterOptions.FilterByCollection, logger, cancellationToken).ConfigureAwait(false); postmanCollection = await JsonDocument.ParseAsync(collectionStream, cancellationToken: cancellationToken).ConfigureAwait(false); } // Load OpenAPI document var format = OpenApiModelFactory.GetFormat(options.OpenApi); - var document = await GetOpenApi(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false); + var document = await GetOpenApiAsync(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false); if (options.FilterOptions != null) { @@ -129,7 +129,7 @@ public static async Task TransformOpenApiDocument(HidiOptions options, ILogger l } } - private static async Task FindApiDependency(string? apiManifestPath, ILogger logger, CancellationToken cancellationToken = default) + private static async Task FindApiDependencyAsync(string? apiManifestPath, ILogger logger, CancellationToken cancellationToken = default) { ApiDependency? apiDependency = null; // If API Manifest is provided, load it, use it get the OpenAPI path @@ -143,7 +143,7 @@ public static async Task TransformOpenApiDocument(HidiOptions options, ILogger l { apiDependencyName = apiManifestRef[1]; } - using (var fileStream = await GetStream(apiManifestRef[0], logger, cancellationToken).ConfigureAwait(false)) + using (var fileStream = await GetStreamAsync(apiManifestRef[0], logger, cancellationToken).ConfigureAwait(false)) { var document = await JsonDocument.ParseAsync(fileStream, cancellationToken: cancellationToken).ConfigureAwait(false); apiManifest = ApiManifestDocument.Load(document.RootElement); @@ -225,7 +225,7 @@ private static void WriteOpenApi(HidiOptions options, OpenApiFormat openApiForma } // Get OpenAPI document either from OpenAPI or CSDL - private static async Task GetOpenApi(HidiOptions options, string format, ILogger logger, string? metadataVersion = null, CancellationToken cancellationToken = default) + private static async Task GetOpenApiAsync(HidiOptions options, string format, ILogger logger, string? metadataVersion = null, CancellationToken cancellationToken = default) { OpenApiDocument document; Stream stream; @@ -236,7 +236,7 @@ private static async Task GetOpenApi(HidiOptions options, strin using (logger.BeginScope("Convert CSDL: {Csdl}", options.Csdl)) { stopwatch.Start(); - stream = await GetStream(options.Csdl, logger, cancellationToken).ConfigureAwait(false); + stream = await GetStreamAsync(options.Csdl, logger, cancellationToken).ConfigureAwait(false); Stream? filteredStream = null; if (!string.IsNullOrEmpty(options.CsdlFilter)) { @@ -246,15 +246,15 @@ private static async Task GetOpenApi(HidiOptions options, strin await stream.DisposeAsync().ConfigureAwait(false); } - document = await ConvertCsdlToOpenApi(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false); + document = await ConvertCsdlToOpenApiAsync(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false); stopwatch.Stop(); logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } } else if (!string.IsNullOrEmpty(options.OpenApi)) { - stream = await GetStream(options.OpenApi, logger, cancellationToken).ConfigureAwait(false); - var result = await ParseOpenApi(options.OpenApi, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false); + stream = await GetStreamAsync(options.OpenApi, logger, cancellationToken).ConfigureAwait(false); + var result = await ParseOpenApiAsync(options.OpenApi, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false); document = result.OpenApiDocument; } else throw new InvalidOperationException("No input file path or URL provided"); @@ -336,7 +336,7 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe /// Implementation of the validate command /// /// when valid, when invalid and when cancelled - public static async Task ValidateOpenApiDocument( + public static async Task ValidateOpenApiDocumentAsync( string openApi, ILogger logger, CancellationToken cancellationToken = default) @@ -350,9 +350,9 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe try { - using var stream = await GetStream(openApi, logger, cancellationToken).ConfigureAwait(false); + using var stream = await GetStreamAsync(openApi, logger, cancellationToken).ConfigureAwait(false); - result = await ParseOpenApi(openApi, false, logger, stream, cancellationToken).ConfigureAwait(false); + result = await ParseOpenApiAsync(openApi, false, logger, stream, cancellationToken).ConfigureAwait(false); using (logger.BeginScope("Calculating statistics")) { @@ -380,7 +380,7 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe return result.OpenApiDiagnostic.Errors.Count == 0; } - private static async Task ParseOpenApi(string openApiFile, 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(); @@ -413,7 +413,7 @@ private static async Task ParseOpenApi(string openApiFile, bool inli /// /// The CSDL stream. /// An OpenAPI document. - public static async Task ConvertCsdlToOpenApi(Stream csdl, string format, string? metadataVersion = null, IConfiguration? settings = null, CancellationToken token = default) + public static async Task ConvertCsdlToOpenApiAsync(Stream csdl, string format, string? metadataVersion = null, IConfiguration? settings = null, CancellationToken token = default) { using var reader = new StreamReader(csdl); var csdlText = await reader.ReadToEndAsync(token).ConfigureAwait(false); @@ -502,7 +502,7 @@ private static Dictionary> EnumerateJsonDocument(JsonElemen /// /// Reads stream from file system or makes HTTP request depending on the input string /// - private static async Task GetStream(string input, ILogger logger, CancellationToken cancellationToken = default) + private static async Task GetStreamAsync(string input, ILogger logger, CancellationToken cancellationToken = default) { Stream stream; using (logger.BeginScope("Reading input stream")) @@ -578,7 +578,7 @@ private static string GetInputPathExtension(string? openapi = null, string? csdl return extension; } - internal static async Task ShowOpenApiDocument(HidiOptions options, ILogger logger, CancellationToken cancellationToken = default) + internal static async Task ShowOpenApiDocumentAsync(HidiOptions options, ILogger logger, CancellationToken cancellationToken = default) { try { @@ -588,7 +588,7 @@ private static string GetInputPathExtension(string? openapi = null, string? csdl } var format = OpenApiModelFactory.GetFormat(options.OpenApi); - var document = await GetOpenApi(options, format, logger, null, cancellationToken).ConfigureAwait(false); + var document = await GetOpenApiAsync(options, format, logger, null, cancellationToken).ConfigureAwait(false); using (logger.BeginScope("Creating diagram")) { @@ -739,10 +739,10 @@ internal static void WriteTreeDocumentAsHtml(string sourceUrl, OpenApiDocument d writer.WriteLine("netstandard2.0 latest true - 1.6.15 + 1.6.22 OpenAPI.NET Readers for JSON and YAML documents true @@ -24,6 +24,12 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/src/Microsoft.OpenApi.Workbench/MainModel.cs b/src/Microsoft.OpenApi.Workbench/MainModel.cs index d9b2a0fa1..662c98dd3 100644 --- a/src/Microsoft.OpenApi.Workbench/MainModel.cs +++ b/src/Microsoft.OpenApi.Workbench/MainModel.cs @@ -201,7 +201,7 @@ protected void OnPropertyChanged(string propertyName) /// The core method of the class. /// Runs the parsing and serializing. /// - internal async Task ParseDocument() + internal async Task ParseDocumentAsync() { Stream stream = null; try @@ -293,7 +293,7 @@ internal async Task ParseDocument() if (stream != null) { stream.Close(); - stream.Dispose(); + await stream.DisposeAsync(); } } diff --git a/src/Microsoft.OpenApi.Workbench/MainWindow.xaml.cs b/src/Microsoft.OpenApi.Workbench/MainWindow.xaml.cs index c42c263a6..3c02c254d 100644 --- a/src/Microsoft.OpenApi.Workbench/MainWindow.xaml.cs +++ b/src/Microsoft.OpenApi.Workbench/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -19,13 +19,14 @@ public MainWindow() DataContext = _mainModel; } +#pragma warning disable VSTHRD100 // Avoid async void methods private async void Button_Click(object sender, RoutedEventArgs e) +#pragma warning restore VSTHRD100 // Avoid async void methods { try { - await _mainModel.ParseDocument(); - } - catch (Exception ex) + await _mainModel.ParseDocumentAsync(); + } catch (Exception ex) { _mainModel.Errors = ex.Message; } diff --git a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj index bb97177a9..5545dc84f 100644 --- a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj +++ b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj @@ -8,8 +8,13 @@ true - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs b/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs new file mode 100644 index 000000000..538ed521e --- /dev/null +++ b/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs @@ -0,0 +1,425 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +#nullable enable + +// This collection of attribute definitions are helpers for accessing trim-related attributes in +// projects targeting .NET 6 or lower. Since the trimmer queries for these attributes by name, having +// these attributes source included is sufficient for the trimmer to recognize them. For more information +// on this approach, see https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#approach-2-define-the-attributes-internally. +namespace System.Diagnostics.CodeAnalysis +{ +#if !NET7_0_OR_GREATER + /// + /// Indicates that the specified method requires the ability to generate new code at runtime, + /// for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when compiling ahead of time. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresDynamicCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of dynamic code. + /// + public RequiresDynamicCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of dynamic code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires dynamic code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } +#endif + +#if !NET5_0_OR_GREATER + /// + /// Indicates that the specified method requires dynamic access to code that is not referenced + /// statically, for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when removing unreferenced + /// code from an application. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresUnreferencedCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of unreferenced code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } + + /// + /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + /// single code artifact. + /// + /// + /// is different than + /// in that it doesn't have a + /// . So it is always preserved in the compiled assembly. + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class UnconditionalSuppressMessageAttribute : Attribute + { + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } + } + + /// + /// States a dependency that one member has on another. + /// + /// + /// This can be used to inform tooling of a dependency that is otherwise not evident purely from + /// metadata and IL, for example a member relied on via reflection. + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + internal sealed class DynamicDependencyAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on the same type as the consumer. + /// + /// The signature of the member depended on. + public DynamicDependencyAttribute(string memberSignature) + { + MemberSignature = memberSignature; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a . + /// + /// The signature of the member depended on. + /// The containing . + public DynamicDependencyAttribute(string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a type in an assembly. + /// + /// The signature of the member depended on. + /// The full name of the type containing the specified member. + /// The assembly name of the type containing the specified member. + public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a . + /// + /// The types of members depended on. + /// The containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a type in an assembly. + /// + /// The types of members depended on. + /// The full name of the type containing the specified members. + /// The assembly name of the type containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Gets the signature of the member depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public string? MemberSignature { get; } + + /// + /// Gets the which specifies the type + /// of members depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + /// + /// Gets the containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public Type? Type { get; } + + /// + /// Gets the full name of the type containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public string? TypeName { get; } + + /// + /// Gets the assembly name of the specified type. + /// + /// + /// is only valid when is specified. + /// + public string? AssemblyName { get; } + + /// + /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". + /// + public string? Condition { get; set; } + } + + /// + /// Indicates that certain members on a specified are accessed dynamically, + /// for example through . + /// + /// + /// This allows tools to understand which members are being accessed during the execution + /// of a program. + /// + /// This attribute is valid on members whose type is or . + /// + /// When this attribute is applied to a location of type , the assumption is + /// that the string represents a fully qualified type name. + /// + /// When this attribute is applied to a class, interface, or struct, the members specified + /// can be accessed dynamically on instances returned from calling + /// on instances of that class, interface, or struct. + /// + /// If the attribute is applied to a method it's treated as a special case and it implies + /// the attribute should be applied to the "this" parameter of the method. As such the attribute + /// should only be used on instance methods of types assignable to System.Type (or string, but no methods + /// will use it there). + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] + internal sealed class DynamicallyAccessedMembersAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified member types. + /// + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } + + /// + /// Specifies the types of members that are dynamically accessed. + /// + /// This enumeration has a attribute that allows a + /// bitwise combination of its member values. + /// + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all interfaces implemented by the type. + /// + Interfaces = 0x2000, + + /// + /// Specifies all members. + /// + All = ~None + } +#endif +} diff --git a/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs b/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs index 4e2e795d3..bc4e86783 100644 --- a/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs +++ b/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using System; +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Microsoft.OpenApi.Attributes; @@ -13,6 +15,9 @@ namespace Microsoft.OpenApi.Extensions /// public static class EnumExtensions { + // Cache to store display names of enum values + private static readonly ConcurrentDictionary DisplayNameCache = new(); + /// /// Gets an attribute on an enum field value. /// @@ -21,10 +26,17 @@ public static class EnumExtensions /// /// The attribute of the specified type or null. /// + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fields are never trimmed for enum types.")] public static T GetAttributeOfType(this Enum enumValue) where T : Attribute { var type = enumValue.GetType(); - var memInfo = type.GetMember(enumValue.ToString()).First(); + // Use GetField to get the field info for the enum value + var memInfo = type.GetField(enumValue.ToString(), BindingFlags.Public | BindingFlags.Static); + + if (memInfo == null) + return null; + + // Retrieve the custom attributes of type T var attributes = memInfo.GetCustomAttributes(false); return attributes.FirstOrDefault(); } @@ -34,13 +46,20 @@ public static T GetAttributeOfType(this Enum enumValue) where T : Attribute /// /// The enum value. /// - /// Use if exists. + /// Use if it exists. /// Otherwise, use the standard string representation. /// public static string GetDisplayName(this Enum enumValue) { - var attribute = enumValue.GetAttributeOfType(); - return attribute == null ? enumValue.ToString() : attribute.Name; + // Retrieve the display name from the cache if it exists + return DisplayNameCache.GetOrAdd(enumValue, e => + { + // Get the DisplayAttribute + var attribute = e.GetAttributeOfType(); + + // Return the DisplayAttribute name if it exists, otherwise return the enum's string representation + return attribute == null ? e.ToString() : attribute.Name; + }); } } } diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiServerExtensions.cs b/src/Microsoft.OpenApi/Extensions/OpenApiServerExtensions.cs new file mode 100644 index 000000000..b885cb235 --- /dev/null +++ b/src/Microsoft.OpenApi/Extensions/OpenApiServerExtensions.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Extensions; + +/// +/// Extension methods for serialization. +/// +public static class OpenApiServerExtensions +{ + /// + /// Replaces URL variables in a server's URL + /// + /// The OpenAPI server object + /// The server variable values that will be used to replace the default values. + /// A URL with the provided variables substituted. + /// + /// Thrown when: + /// 1. A substitution has no valid value in both the supplied dictionary and the default + /// 2. A substitution's value is not available in the enum provided + /// + public static string ReplaceServerUrlVariables(this OpenApiServer server, IDictionary values = null) + { + var parsedUrl = server.Url; + foreach (var variable in server.Variables) + { + // Try to get the value from the provided values + if (values is not { } v || !v.TryGetValue(variable.Key, out var value) || string.IsNullOrEmpty(value)) + { + // Fall back to the default value + value = variable.Value.Default; + } + + // Validate value + if (string.IsNullOrEmpty(value)) + { + // According to the spec, the variable's default value is required. + // This code path should be hit when a value isn't provided & a default value isn't available + throw new ArgumentException( + string.Format(SRResource.ParseServerUrlDefaultValueNotAvailable, variable.Key), nameof(server)); + } + + // If an enum is provided, the array should not be empty & the value should exist in the enum + if (variable.Value.Enum is {} e && (e.Count == 0 || !e.Contains(value))) + { + throw new ArgumentException( + string.Format(SRResource.ParseServerUrlValueNotValid, value, variable.Key), nameof(values)); + } + + parsedUrl = parsedUrl.Replace($"{{{variable.Key}}}", value); + } + + return parsedUrl; + } +} diff --git a/src/Microsoft.OpenApi/Extensions/StringExtensions.cs b/src/Microsoft.OpenApi/Extensions/StringExtensions.cs index 51ce37365..541523df5 100644 --- a/src/Microsoft.OpenApi/Extensions/StringExtensions.cs +++ b/src/Microsoft.OpenApi/Extensions/StringExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.OpenApi.Attributes; @@ -16,7 +17,7 @@ public static class StringExtensions /// Gets the enum value based on the given enum type and display name. /// /// The display name. - public static T GetEnumFromDisplayName(this string displayName) + public static T GetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName) { var type = typeof(T); if (!type.IsEnum) @@ -24,14 +25,12 @@ public static T GetEnumFromDisplayName(this string displayName) return default; } - foreach (var value in Enum.GetValues(type)) + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { - var field = type.GetField(value.ToString()); - var displayAttribute = (DisplayAttribute)field.GetCustomAttribute(typeof(DisplayAttribute)); if (displayAttribute != null && displayAttribute.Name == displayName) { - return (T)value; + return (T)field.GetValue(null); } } diff --git a/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs b/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs new file mode 100644 index 000000000..dc1ee84a0 --- /dev/null +++ b/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.OpenApi.Interfaces +{ + /// + /// Represents an Open API element that can be annotated with + /// non-serializable properties in a property bag. + /// + public interface IOpenApiAnnotatable + { + /// + /// A collection of properties associated with the current OpenAPI element. + /// + IDictionary Annotations { get; set; } + } +} diff --git a/src/Microsoft.OpenApi/Interfaces/IStreamLoader.cs b/src/Microsoft.OpenApi/Interfaces/IStreamLoader.cs index cdf7eaaf8..c3edebe1b 100644 --- a/src/Microsoft.OpenApi/Interfaces/IStreamLoader.cs +++ b/src/Microsoft.OpenApi/Interfaces/IStreamLoader.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.ComponentModel; using System.IO; using System.Threading.Tasks; using Microsoft.OpenApi.Models; @@ -25,6 +26,8 @@ public interface IStreamLoader /// /// /// + [Obsolete("Use the Async overload")] + [EditorBrowsable(EditorBrowsableState.Never)] Stream Load(Uri uri); } } diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index a4cc8e4b3..b6ccd1796 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -1,9 +1,9 @@ - + netstandard2.0 Latest true - 1.6.15 + 1.6.22 .NET models with JSON and YAML writers for OpenAPI specification true @@ -21,8 +21,8 @@ true - - + + @@ -43,4 +43,11 @@ + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 2c165ed33..0baf31e68 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -23,7 +23,7 @@ namespace Microsoft.OpenApi.Models /// /// Describes an OpenAPI object (OpenAPI document). See: https://swagger.io/specification /// - public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable { /// /// Related workspace containing components that are referenced in a document @@ -88,6 +88,9 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// public string HashCode => GenerateHashValue(this); + /// + public IDictionary? Annotations { get; set; } + /// /// Implements IBaseDocument /// @@ -120,6 +123,7 @@ public OpenApiDocument(OpenApiDocument? document) Tags = document?.Tags != null ? new List(document.Tags) : null; ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null; Extensions = document?.Extensions != null ? new Dictionary(document.Extensions) : null; + Annotations = document?.Annotations != null ? new Dictionary(document.Annotations) : null; BaseUri = document?.BaseUri != null ? document.BaseUri : new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid()); } @@ -355,14 +359,7 @@ public void SerializeAsV2(IOpenApiWriter writer) private static string ParseServerUrl(OpenApiServer server) { - var parsedUrl = server.Url; - - var variables = server.Variables; - foreach (var variable in variables.Where(static x => !string.IsNullOrEmpty(x.Value.Default))) - { - parsedUrl = parsedUrl.Replace($"{{{variable.Key}}}", variable.Value.Default); - } - return parsedUrl; + return server.ReplaceServerUrlVariables(new Dictionary(0)); } private static void WriteHostInfoV2(IOpenApiWriter writer, IList? servers) diff --git a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs index b1e633dd9..268878b1b 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs index 17af31e64..671a0dcfc 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Nodes; using Microsoft.OpenApi.Helpers; using Microsoft.OpenApi.Interfaces; @@ -103,7 +104,10 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, e) => w.WriteAny(e)); // examples - writer.WriteOptionalMap(OpenApiConstants.Examples, Examples, callback); + if (Examples != null && Examples.Any()) + { + SerializeExamples(writer, Examples); + } // encoding writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback); @@ -121,5 +125,33 @@ public void SerializeAsV2(IOpenApiWriter writer) { // Media type does not exist in V2. } + + private static void SerializeExamples(IOpenApiWriter writer, IDictionary examples) + { + /* Special case for writing out empty arrays as valid response examples + * Check if there is any example with an empty array as its value and set the flag `hasEmptyArray` to true + * */ + var hasEmptyArray = examples.Values.Any( static example => + example.Value is JsonArray arr && arr.Count == 0 + ); + + if (hasEmptyArray) + { + writer.WritePropertyName(OpenApiConstants.Examples); + writer.WriteStartObject(); + foreach (var kvp in examples.Where(static kvp => kvp.Value.Value is JsonArray arr && arr.Count == 0)) + { + writer.WritePropertyName(kvp.Key); + writer.WriteStartObject(); + writer.WriteRequiredObject(OpenApiConstants.Value, kvp.Value.Value, (w, v) => w.WriteAny(v)); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + else + { + writer.WriteOptionalMap(OpenApiConstants.Examples, examples, (w, e) => e.SerializeAsV3(w)); + } + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 4da68d082..6e54cd894 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -15,7 +15,7 @@ namespace Microsoft.OpenApi.Models /// /// Operation Object. /// - public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible + public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable { /// /// Default value for . @@ -108,6 +108,9 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary? Extensions { get; set; } = new Dictionary(); + /// + public IDictionary? Annotations { get; set; } + /// /// Parameterless constructor /// @@ -131,6 +134,7 @@ public OpenApiOperation(OpenApiOperation? operation) Security = operation?.Security != null ? new List(operation.Security) : null; Servers = operation?.Servers != null ? new List(operation.Servers) : null; Extensions = operation?.Extensions != null ? new Dictionary(operation.Extensions) : null; + Annotations = operation?.Annotations != null ? new Dictionary(operation.Annotations) : null; } /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 121292f1e..be68e122a 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs index 1fc206bd3..8a1ae4a43 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -100,7 +100,7 @@ public string ReferenceV3 return Id; } - return "#/components/" + Type.GetDisplayName() + "/" + Id; + return "#/components/" + Type.Value.GetDisplayName() + "/" + Id; } } @@ -187,13 +187,6 @@ private void SerializeInternal(IOpenApiWriter writer) return; } - if (Type == ReferenceType.SecurityScheme) - { - // Write the string as property name - writer.WritePropertyName(ReferenceV3); - return; - } - writer.WriteStartObject(); // $ref @@ -245,7 +238,10 @@ private string GetExternalReferenceV3() return Id; } - return ExternalResource + "#/components/" + Type.GetDisplayName() + "/" + Id; + if (Type.HasValue) + { + return ExternalResource + "#/components/" + Type.Value.GetDisplayName() + "/"+ Id; + } } return ExternalResource; diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index eda8249dc..1adfc8c01 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -14,7 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// The Schema Object allows the definition of input and output data types. /// - public class OpenApiSchema : IOpenApiExtensible, IOpenApiReferenceable, IOpenApiSerializable + public class OpenApiSchema : IOpenApiAnnotatable, IOpenApiExtensible, IOpenApiReferenceable, IOpenApiSerializable { private JsonNode _example; private JsonNode _default; @@ -341,6 +341,9 @@ public virtual IList Examples /// public virtual OpenApiReference Reference { get; set; } + /// + public IDictionary Annotations { get; set; } + /// /// Parameterless constructor /// @@ -404,6 +407,7 @@ public OpenApiSchema(OpenApiSchema schema) Extensions = schema?.Extensions != null ? new Dictionary(schema.Extensions) : null; UnresolvedReference = schema?.UnresolvedReference ?? UnresolvedReference; Reference = schema?.Reference != null ? new(schema?.Reference) : null; + Annotations = schema?.Annotations != null ? new Dictionary(schema?.Annotations) : null; } /// @@ -884,7 +888,10 @@ private void DowncastTypeArrayToV2OrV3(string[] array, IOpenApiWriter writer, Op // Find the non-null value and write it out var nonNullValue = array.First(v => v != OpenApiConstants.Null); writer.WriteProperty(OpenApiConstants.Type, nonNullValue); - writer.WriteProperty(nullableProp, true); + if (!Nullable) + { + writer.WriteProperty(nullableProp, true); + } } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs index d78a4d8e3..428d0649e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs @@ -67,7 +67,7 @@ private void SerializeInternal(IOpenApiWriter writer, Action /// An enumeration of string values to be used if the substitution options are from a limited set. /// - public List Enum { get; set; } = new(); + /// + /// If the server variable in the OpenAPI document has no enum member, this property will be null. + /// + public List Enum { get; set; } /// /// This object MAY be extended with Specification Extensions. diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 8ec2f99c8..86334cd1e 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -233,6 +232,24 @@ internal static string OpenApiWriterExceptionGenericError { } } + /// + /// Looks up a localized string similar to Invalid server variable '{0}'. A value was not provided and no default value was provided.. + /// + internal static string ParseServerUrlDefaultValueNotAvailable { + get { + return ResourceManager.GetString("ParseServerUrlDefaultValueNotAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value '{0}' is not a valid value for variable '{1}'. If an enum is provided, it should not be empty and the value provided should exist in the enum. + /// + internal static string ParseServerUrlValueNotValid { + get { + return ResourceManager.GetString("ParseServerUrlValueNotValid", resourceCulture); + } + } + /// /// Looks up a localized string similar to The given primitive type '{0}' is not supported.. /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index f0bb497d3..0effa1d44 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -225,4 +225,10 @@ OpenAPI document must be added to an OpenApiWorkspace to be able to resolve external references. + + Invalid server variable '{0}'. A value was not provided and no default value was provided. + + + Value '{0}' is not a valid value for variable '{1}'. If an enum is provided, it should not be empty and the value provided should exist in the enum + diff --git a/src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs b/src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs index fd17a3643..27aad722e 100644 --- a/src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs +++ b/src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs @@ -86,7 +86,7 @@ public async Task ReadAsync(JsonNode jsonNode, if (settings.LoadExternalRefs) { - var diagnosticExternalRefs = await LoadExternalRefs(document, cancellationToken, settings, format); + var diagnosticExternalRefs = await LoadExternalRefsAsync(document, cancellationToken, settings, format); // Merge diagnostics of external reference if (diagnosticExternalRefs != null) { @@ -189,7 +189,7 @@ private JsonNode LoadJsonNodes(TextReader input) return nodes; } - private async Task LoadExternalRefs(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null) + private async Task LoadExternalRefsAsync(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null) { // Create workspace for all documents to live in. var baseUrl = settings.BaseUrl ?? new Uri(OpenApiConstants.BaseRegistryUri); diff --git a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs index 6aeaa8067..ddabdc6be 100644 --- a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs +++ b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs @@ -33,7 +33,9 @@ static OpenApiModelFactory() /// An OpenAPI document instance. public static ReadResult Load(string url, OpenApiReaderSettings settings = null) { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits return LoadAsync(url, settings).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits } /// @@ -49,7 +51,10 @@ public static ReadResult Load(Stream stream, { settings ??= new OpenApiReaderSettings(); +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits var result = LoadAsync(stream, format, settings).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + if (!settings.LeaveStreamOpen) { stream.Dispose(); @@ -69,7 +74,10 @@ public static ReadResult Load(TextReader input, string format, OpenApiReaderSettings settings = null) { - return LoadAsync(input, format, settings).GetAwaiter().GetResult(); +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + var result = LoadAsync(input, format, settings).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return result; } /// @@ -81,7 +89,7 @@ public static ReadResult Load(TextReader input, public static async Task LoadAsync(string url, OpenApiReaderSettings settings = null) { var format = GetFormat(url); - var stream = await GetStream(url); + var stream = await GetStreamAsync(url); return await LoadAsync(stream, format, settings); } @@ -148,7 +156,26 @@ public static ReadResult Parse(string input, format ??= OpenApiConstants.Json; settings ??= new OpenApiReaderSettings(); using var reader = new StringReader(input); - return LoadAsync(reader, format, settings).GetAwaiter().GetResult(); + +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + return ParseAsync(input, reader, format, settings).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + } + + /// + /// An Async method to prevent synchornously blocking the calling thread. + /// + /// + /// + /// + /// + /// + public static async Task ParseAsync(string input, + StringReader reader, + string format = null, + OpenApiReaderSettings settings = null) + { + return await LoadAsync(reader, format, settings); } /// @@ -186,7 +213,11 @@ public static T Load(string url, OpenApiSpecVersion version, out OpenApiDiagn { var format = GetFormat(url); settings ??= new OpenApiReaderSettings(); - var stream = GetStream(url).GetAwaiter().GetResult(); + +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + var stream = GetStreamAsync(url).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return Load(stream, version, format, out diagnostic, settings); } @@ -230,7 +261,10 @@ private static string GetContentType(string url) { if (!string.IsNullOrEmpty(url)) { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits var response = _httpClient.GetAsync(url).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + var mediaType = response.Content.Headers.ContentType.MediaType; return mediaType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); } @@ -263,7 +297,7 @@ public static string GetFormat(string url) return null; } - private static async Task GetStream(string url) + private static async Task GetStreamAsync(string url) { Stream stream; if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) @@ -300,6 +334,5 @@ SecurityException or return stream; } - } } diff --git a/src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs b/src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs index 7c8888abb..746ca0c96 100644 --- a/src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs +++ b/src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.ComponentModel; using System.IO; using System.Net.Http; using System.Threading.Tasks; @@ -26,26 +27,15 @@ public DefaultStreamLoader(Uri baseUrl) { this.baseUrl = baseUrl; } +/// - /// - /// Loads a document stream from the input URL - /// - /// - /// - /// + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] public Stream Load(Uri uri) { - var absoluteUri = new Uri(baseUrl, uri); - switch (uri.Scheme) - { - case "file": - return File.OpenRead(absoluteUri.AbsolutePath); - case "http": - case "https": - return _httpClient.GetStreamAsync(absoluteUri).GetAwaiter().GetResult(); - default: - throw new ArgumentException("Unsupported scheme"); - } +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + return LoadAsync(uri).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits } /// diff --git a/src/Microsoft.OpenApi/Services/CopyReferences.cs b/src/Microsoft.OpenApi/Services/CopyReferences.cs index 757471466..73bb667b6 100644 --- a/src/Microsoft.OpenApi/Services/CopyReferences.cs +++ b/src/Microsoft.OpenApi/Services/CopyReferences.cs @@ -26,8 +26,8 @@ public override void Visit(IOpenApiReferenceable referenceable) switch (referenceable) { case OpenApiSchema schema: - EnsureComponentsExists(); - EnsureSchemasExists(); + EnsureComponentsExist(); + EnsureSchemasExist(); if (!Components.Schemas.ContainsKey(schema.Reference.Id)) { Components.Schemas.Add(schema.Reference.Id, schema); @@ -35,8 +35,8 @@ public override void Visit(IOpenApiReferenceable referenceable) break; case OpenApiParameter parameter: - EnsureComponentsExists(); - EnsureParametersExists(); + EnsureComponentsExist(); + EnsureParametersExist(); if (!Components.Parameters.ContainsKey(parameter.Reference.Id)) { Components.Parameters.Add(parameter.Reference.Id, parameter); @@ -44,8 +44,8 @@ public override void Visit(IOpenApiReferenceable referenceable) break; case OpenApiResponse response: - EnsureComponentsExists(); - EnsureResponsesExists(); + EnsureComponentsExist(); + EnsureResponsesExist(); if (!Components.Responses.ContainsKey(response.Reference.Id)) { Components.Responses.Add(response.Reference.Id, response); @@ -53,18 +53,64 @@ public override void Visit(IOpenApiReferenceable referenceable) break; case OpenApiRequestBody requestBody: - EnsureComponentsExists(); - EnsureResponsesExists(); - EnsurRequestBodiesExists(); + EnsureComponentsExist(); + EnsureResponsesExist(); + EnsureRequestBodiesExist(); if (!Components.RequestBodies.ContainsKey(requestBody.Reference.Id)) { Components.RequestBodies.Add(requestBody.Reference.Id, requestBody); } break; + case OpenApiExample example: + EnsureComponentsExist(); + EnsureExamplesExist(); + if (!Components.Examples.ContainsKey(example.Reference.Id)) + { + Components.Examples.Add(example.Reference.Id, example); + } + break; + + case OpenApiHeader header: + EnsureComponentsExist(); + EnsureHeadersExist(); + if (!Components.Headers.ContainsKey(header.Reference.Id)) + { + Components.Headers.Add(header.Reference.Id, header); + } + break; + + case OpenApiCallback callback: + EnsureComponentsExist(); + EnsureCallbacksExist(); + if (!Components.Callbacks.ContainsKey(callback.Reference.Id)) + { + Components.Callbacks.Add(callback.Reference.Id, callback); + } + break; + + case OpenApiLink link: + EnsureComponentsExist(); + EnsureLinksExist(); + if (!Components.Links.ContainsKey(link.Reference.Id)) + { + Components.Links.Add(link.Reference.Id, link); + } + break; + + case OpenApiSecurityScheme securityScheme: + EnsureComponentsExist(); + EnsureSecuritySchemesExist(); + if (!Components.SecuritySchemes.ContainsKey(securityScheme.Reference.Id)) + { + Components.SecuritySchemes.Add(securityScheme.Reference.Id, securityScheme); + } + break; + default: break; } + base.Visit(referenceable); } @@ -77,8 +123,8 @@ public override void Visit(OpenApiSchema schema) // This is needed to handle schemas used in Responses in components if (schema.Reference != null) { - EnsureComponentsExists(); - EnsureSchemasExists(); + EnsureComponentsExist(); + EnsureSchemasExist(); if (!Components.Schemas.ContainsKey(schema.Reference.Id)) { Components.Schemas.Add(schema.Reference.Id, schema); @@ -87,41 +133,54 @@ public override void Visit(OpenApiSchema schema) base.Visit(schema); } - private void EnsureComponentsExists() + private void EnsureComponentsExist() { - if (_target.Components == null) - { - _target.Components = new(); - } + _target.Components ??= new(); } - private void EnsureSchemasExists() + private void EnsureSchemasExist() { - if (_target.Components.Schemas == null) - { - _target.Components.Schemas = new Dictionary(); - } + _target.Components.Schemas ??= new Dictionary(); } - private void EnsureParametersExists() + private void EnsureParametersExist() { - if (_target.Components.Parameters == null) - { - _target.Components.Parameters = new Dictionary(); - } + _target.Components.Parameters ??= new Dictionary(); } - private void EnsureResponsesExists() + private void EnsureResponsesExist() { - if (_target.Components.Responses == null) - { - _target.Components.Responses = new Dictionary(); - } + _target.Components.Responses ??= new Dictionary(); } - private void EnsurRequestBodiesExists() + private void EnsureRequestBodiesExist() { _target.Components.RequestBodies ??= new Dictionary(); } + + private void EnsureExamplesExist() + { + _target.Components.Examples ??= new Dictionary(); + } + + private void EnsureHeadersExist() + { + _target.Components.Headers ??= new Dictionary(); + } + + private void EnsureCallbacksExist() + { + _target.Components.Callbacks ??= new Dictionary(); + } + + private void EnsureLinksExist() + { + _target.Components.Links ??= new Dictionary(); + } + + private void EnsureSecuritySchemesExist() + { + _target.Components.SecuritySchemes ??= new Dictionary(); + } } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 06283e49b..22916fd7c 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -61,6 +61,10 @@ public static class OpenApiFilterService public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Func predicate) { // Fetch and copy title, graphVersion and server info from OpenApiDoc + var components = source.Components is null + ? null + : new OpenApiComponents() { SecuritySchemes = source.Components.SecuritySchemes }; + var subset = new OpenApiDocument { Info = new() @@ -74,7 +78,7 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun Extensions = source.Info.Extensions }, - Components = new() { SecuritySchemes = source.Components.SecuritySchemes }, + Components = components, SecurityRequirements = source.SecurityRequirements, Servers = source.Servers }; @@ -108,7 +112,10 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun { foreach (var parameter in result.Parameters) { - pathItem.Parameters.Add(parameter); + if (!pathItem.Parameters.Contains(parameter)) + { + pathItem.Parameters.Add(parameter); + } } } } @@ -274,6 +281,42 @@ private static bool AddReferences(OpenApiComponents newComponents, OpenApiCompon moreStuff = true; target.RequestBodies.Add(item); } + + foreach (var item in newComponents.Headers + .Where(item => !target.Headers.ContainsKey(item.Key))) + { + moreStuff = true; + target.Headers.Add(item); + } + + foreach (var item in newComponents.Links + .Where(item => !target.Links.ContainsKey(item.Key))) + { + moreStuff = true; + target.Links.Add(item); + } + + foreach (var item in newComponents.Callbacks + .Where(item => !target.Callbacks.ContainsKey(item.Key))) + { + moreStuff = true; + target.Callbacks.Add(item); + } + + foreach (var item in newComponents.Examples + .Where(item => !target.Examples.ContainsKey(item.Key))) + { + moreStuff = true; + target.Examples.Add(item); + } + + foreach (var item in newComponents.SecuritySchemes + .Where(item => !target.SecuritySchemes.ContainsKey(item.Key))) + { + moreStuff = true; + target.SecuritySchemes.Add(item); + } + return moreStuff; } diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 8ba63f9ea..ba5d4349d 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs @@ -150,6 +150,13 @@ public OpenApiUrlTreeNode Attach(string path, } var segments = path.Split('/'); + if (path.EndsWith("/", StringComparison.OrdinalIgnoreCase)) + { + // Remove the last element, which is empty, and append the trailing slash to the new last element + // This is to support URLs with trailing slashes + Array.Resize(ref segments, segments.Length - 1); + segments[segments.Length - 1] += @"\"; + } return Attach(segments: segments, pathItem: pathItem, diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs index dd11a661d..35d4b9a25 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs @@ -26,9 +26,32 @@ public static class OpenApiServerRules context.CreateError(nameof(ServerRequiredFields), String.Format(SRResource.Validation_FieldIsRequired, "url", "server")); } + + context.Exit(); + context.Enter("variables"); + foreach (var variable in server.Variables) + { + context.Enter(variable.Key); + ValidateServerVariableRequiredFields(context, variable.Key, variable.Value); + context.Exit(); + } context.Exit(); }); // add more rules + + /// + /// Validate required fields in server variable + /// + private static void ValidateServerVariableRequiredFields(IValidationContext context, string key, OpenApiServerVariable item) + { + context.Enter("default"); + if (string.IsNullOrEmpty(item.Default)) + { + context.CreateError("ServerVariableMustHaveDefaultValue", + String.Format(SRResource.Validation_FieldIsRequired, "default", key)); + } + context.Exit(); + } } } diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index e5950c300..67b84f0be 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -300,16 +300,15 @@ private static ValidationRuleSet BuildDefaultRuleSet() { var ruleSet = new ValidationRuleSet(); var validationRuleType = typeof(ValidationRule); + + var ruleTypeProperties = GetValidationRuleTypes(); - var rules = typeof(ValidationRuleSet).Assembly.GetTypes() - .Where(t => t.IsClass - && t != typeof(object) - && t.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any()) - .SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public) - .Where(p => validationRuleType.IsAssignableFrom(p.PropertyType))); - - foreach (var property in rules) + foreach (var property in ruleTypeProperties) { + if (!validationRuleType.IsAssignableFrom(property.PropertyType)) + { + continue; + } var propertyValue = property.GetValue(null); // static property if (propertyValue is ValidationRule rule) { @@ -319,5 +318,28 @@ private static ValidationRuleSet BuildDefaultRuleSet() return ruleSet; } + + internal static PropertyInfo[] GetValidationRuleTypes() + { + return [ + ..typeof(OpenApiComponentsRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiContactRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiDocumentRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiExtensibleRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiExternalDocsRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiInfoRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiLicenseRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiMediaTypeRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiOAuthFlowRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiServerRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiResponseRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiResponsesRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiSchemaRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiHeaderRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiTagRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiPathsRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ..typeof(OpenApiParameterRules).GetProperties(BindingFlags.Static | BindingFlags.Public), + ]; + } } } diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj index 729b39e68..397831833 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -12,9 +12,10 @@ - - - + + + + @@ -33,4 +34,10 @@ + + + PreserveNewest + + + diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index 5fb1b15f9..99e559e37 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs @@ -3,9 +3,11 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Tests.UtilityFiles; using Moq; +using SharpYaml.Tokens; using Xunit; namespace Microsoft.OpenApi.Hidi.Tests @@ -103,6 +105,57 @@ public void TestPredicateFiltersUsingRelativeRequestUrls() Assert.False(predicate("/foo", OperationType.Patch, null)); } + [Fact] + public void CreateFilteredDocumentUsingPredicateFromRequestUrl() + { + // Arrange + var openApiDocument = new OpenApiDocument + { + Info = new() { Title = "Test", Version = "1.0" }, + Servers = new List { new() { Url = "https://localhost/" } }, + Paths = new() + { + ["/test/{id}"] = new() + { + Operations = new Dictionary + { + { OperationType.Get, new() }, + { OperationType.Patch, new() } + }, + Parameters = new List + { + new() + { + Name = "id", + In = ParameterLocation.Path, + Required = true, + Schema = new() + { + Type = "string" + } + } + } + } + + + } + }; + + var requestUrls = new Dictionary> + { + {"/test/{id}", new List {"GET","PATCH"}} + }; + + // Act + var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: openApiDocument); + var subsetDoc = OpenApiFilterService.CreateFilteredDocument(openApiDocument, predicate); + + // Assert that there's only 1 parameter in the subset document + Assert.NotNull(subsetDoc); + Assert.NotEmpty(subsetDoc.Paths); + Assert.Single(subsetDoc.Paths.First().Value.Parameters); + } + [Fact] public void ShouldParseNestedPostmanCollection() { @@ -170,6 +223,36 @@ public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidArguments Assert.Equal("Cannot specify both operationIds and tags at the same time.", message2); } + [Fact] + public void CopiesOverAllReferencedComponentsToTheSubsetDocumentCorrectly() + { + // Arrange + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "docWithReusableHeadersAndExamples.yaml"); + var operationIds = "getItems"; + + // Act + using var stream = File.OpenRead(filePath); + var doc = OpenApiDocument.Load(stream, "yaml").OpenApiDocument; + + var predicate = OpenApiFilterService.CreatePredicate(operationIds: operationIds); + var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(doc, predicate); + + var response = subsetOpenApiDocument.Paths["/items"].Operations[OperationType.Get]?.Responses?["200"]; + var responseHeader = response?.Headers["x-custom-header"]; + var mediaTypeExample = response?.Content["application/json"]?.Examples?.First().Value; + var targetHeaders = subsetOpenApiDocument.Components?.Headers; + var targetExamples = subsetOpenApiDocument.Components?.Examples; + + // Assert + Assert.Same(doc.Servers, subsetOpenApiDocument.Servers); + Assert.False(responseHeader?.UnresolvedReference); + Assert.False(mediaTypeExample?.UnresolvedReference); + Assert.NotNull(targetHeaders); + Assert.Single(targetHeaders); + Assert.NotNull(targetExamples); + Assert.Single(targetExamples); + } + [Theory] [InlineData("reports.getTeamsUserActivityUserDetail-a3f1", null)] [InlineData(null, "reports.Functions")] diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index 628d83053..798b7532e 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -13,6 +13,7 @@ using Microsoft.OpenApi.OData; using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Hidi.Tests @@ -27,9 +28,48 @@ public OpenApiServiceTests() _logger = new Logger(_loggerFactory); OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader()); OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader()); + } + + [Fact] + public void CreateFilteredDocumentOnMinimalOpenApi() + { + // Arrange + + // We create a minimal OpenApiDocument with a single path and operation. + var openApiDoc = new OpenApiDocument + { + Info = new() + { + Title = "Test", + Version = "1.0.0" + }, + Paths = new() + { + ["/test"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + [OperationType.Get] = new OpenApiOperation() + } + } + } + }; + // Act + var requestUrls = new Dictionary>() + { + { "/test", ["GET"] } + }; + var filterPredicate = OpenApiFilterService.CreatePredicate(null, null, requestUrls, openApiDoc); + var filteredDocument = OpenApiFilterService.CreateFilteredDocument(openApiDoc, filterPredicate); + + // Assert + Assert.NotNull(filteredDocument); + Assert.NotNull(filteredDocument.Paths); + Assert.Single(filteredDocument.Paths); } + [Theory] [InlineData("UtilityFiles/appsettingstest.json")] [InlineData(null)] @@ -95,7 +135,7 @@ public void ShowCommandGeneratesMermaidDiagramAsHtml() } [Fact] - public async Task ShowCommandGeneratesMermaidMarkdownFileWithMermaidDiagram() + public async Task ShowCommandGeneratesMermaidMarkdownFileWithMermaidDiagramAsync() { // create a dummy ILogger instance for testing var options = new HidiOptions @@ -104,81 +144,81 @@ public async Task ShowCommandGeneratesMermaidMarkdownFileWithMermaidDiagram() Output = new("sample.md") }; - await OpenApiService.ShowOpenApiDocument(options, _logger); + await OpenApiService.ShowOpenApiDocumentAsync(options, _logger); var output = await File.ReadAllTextAsync(options.Output.FullName); Assert.Contains("graph LR", output, StringComparison.Ordinal); } [Fact] - public async Task ShowCommandGeneratesMermaidHtmlFileWithMermaidDiagram() + public async Task ShowCommandGeneratesMermaidHtmlFileWithMermaidDiagramAsync() { var options = new HidiOptions { OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml") }; - var filePath = await OpenApiService.ShowOpenApiDocument(options, _logger); + var filePath = await OpenApiService.ShowOpenApiDocumentAsync(options, _logger); Assert.True(File.Exists(filePath)); } [Fact] - public Task ThrowIfOpenApiUrlIsNotProvidedWhenValidating() + public Task ThrowIfOpenApiUrlIsNotProvidedWhenValidatingAsync() { return Assert.ThrowsAsync(() => - OpenApiService.ValidateOpenApiDocument("", _logger)); + OpenApiService.ValidateOpenApiDocumentAsync("", _logger)); } [Fact] - public Task ThrowIfURLIsNotResolvableWhenValidating() + public Task ThrowIfURLIsNotResolvableWhenValidatingAsync() { return Assert.ThrowsAsync(() => - OpenApiService.ValidateOpenApiDocument("https://example.org/itdoesnmatter", _logger)); + OpenApiService.ValidateOpenApiDocumentAsync("https://example.org/itdoesnmatter", _logger)); } [Fact] - public Task ThrowIfFileDoesNotExistWhenValidating() + public Task ThrowIfFileDoesNotExistWhenValidatingAsync() { return Assert.ThrowsAsync(() => - OpenApiService.ValidateOpenApiDocument("aFileThatBetterNotExist.fake", _logger)); + OpenApiService.ValidateOpenApiDocumentAsync("aFileThatBetterNotExist.fake", _logger)); } [Fact] - public async Task ValidateCommandProcessesOpenApi() + public async Task ValidateCommandProcessesOpenApiAsync() { // create a dummy ILogger instance for testing - await OpenApiService.ValidateOpenApiDocument(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger); + await OpenApiService.ValidateOpenApiDocumentAsync(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger); Assert.True(true); } [Fact] - public async Task ValidFileReturnsTrue() + public async Task ValidFileReturnsTrueAsync() { - var isValid = await OpenApiService.ValidateOpenApiDocument(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger); + var isValid = await OpenApiService.ValidateOpenApiDocumentAsync(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger); Assert.True(isValid); } [Fact] - public async Task InvalidFileReturnsFalse() + public async Task InvalidFileReturnsFalseAsync() { - var isValid = await OpenApiService.ValidateOpenApiDocument(Path.Combine("UtilityFiles", "InvalidSampleOpenApi.yml"), _logger); + var isValid = await OpenApiService.ValidateOpenApiDocumentAsync(Path.Combine("UtilityFiles", "InvalidSampleOpenApi.yml"), _logger); Assert.False(isValid); } [Fact] - public async Task CancellingValidationReturnsNull() + public async Task CancellingValidationReturnsNullAsync() { using var cts = new CancellationTokenSource(); await cts.CancelAsync(); - var isValid = await OpenApiService.ValidateOpenApiDocument(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger, cts.Token); + var isValid = await OpenApiService.ValidateOpenApiDocumentAsync(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger, cts.Token); Assert.Null(isValid); } [Fact] - public async Task TransformCommandConvertsOpenApi() + public async Task TransformCommandConvertsOpenApiAsync() { var options = new HidiOptions { @@ -190,7 +230,7 @@ public async Task TransformCommandConvertsOpenApi() InlineExternal = false, }; // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument(options, _logger); + await OpenApiService.TransformOpenApiDocumentAsync(options, _logger); var output = await File.ReadAllTextAsync("sample.json"); Assert.NotEmpty(output); @@ -198,7 +238,7 @@ public async Task TransformCommandConvertsOpenApi() [Fact] - public async Task TransformCommandConvertsOpenApiWithDefaultOutputName() + public async Task TransformCommandConvertsOpenApiWithDefaultOutputNameAsync() { var options = new HidiOptions { @@ -209,14 +249,14 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputName() InlineExternal = false, }; // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument(options, _logger); + await OpenApiService.TransformOpenApiDocumentAsync(options, _logger); var output = await File.ReadAllTextAsync("output.yml"); Assert.NotEmpty(output); } [Fact] - public async Task TransformCommandConvertsOpenApiWithDefaultOutputNameAndSwitchFormat() + public async Task TransformCommandConvertsOpenApiWithDefaultOutputNameAndSwitchFormatAsync() { var options = new HidiOptions { @@ -229,14 +269,14 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputNameAndSwitchF InlineExternal = false, }; // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument(options, _logger); + await OpenApiService.TransformOpenApiDocumentAsync(options, _logger); var output = await File.ReadAllTextAsync("output.yml"); Assert.NotEmpty(output); } [Fact] - public Task ThrowTransformCommandIfOpenApiAndCsdlAreEmpty() + public Task ThrowTransformCommandIfOpenApiAndCsdlAreEmptyAsync() { var options = new HidiOptions { @@ -246,11 +286,11 @@ public Task ThrowTransformCommandIfOpenApiAndCsdlAreEmpty() InlineExternal = false, }; return Assert.ThrowsAsync(() => - OpenApiService.TransformOpenApiDocument(options, _logger)); + OpenApiService.TransformOpenApiDocumentAsync(options, _logger)); } [Fact] - public async Task TransformToPowerShellCompliantOpenApi() + public async Task TransformToPowerShellCompliantOpenApiAsync() { var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "examplepowershellsettings.json"); var options = new HidiOptions @@ -265,31 +305,31 @@ public async Task TransformToPowerShellCompliantOpenApi() SettingsConfig = SettingsUtilities.GetConfiguration(settingsPath) }; // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument(options, _logger); + await OpenApiService.TransformOpenApiDocumentAsync(options, _logger); var output = await File.ReadAllTextAsync("output.yaml"); Assert.NotEmpty(output); } [Fact] - public void InvokeTransformCommand() + public async Task InvokeTransformCommandAsync() { var rootCommand = Program.CreateRootCommand(); var openapi = Path.Combine(".", "UtilityFiles", "SampleOpenApi.yml"); var args = new[] { "transform", "-d", openapi, "-o", "sample.json", "--co" }; var parseResult = rootCommand.Parse(args); - var handler = rootCommand.Subcommands.Where(c => c.Name == "transform").First().Handler; + var handler = rootCommand.Subcommands.First(c => c.Name == "transform").Handler; var context = new InvocationContext(parseResult); - handler!.Invoke(context); + await handler!.InvokeAsync(context); - var output = File.ReadAllText("sample.json"); + var output = await File.ReadAllTextAsync("sample.json"); Assert.NotEmpty(output); } [Fact] - public void InvokeShowCommand() + public async Task InvokeShowCommandAsync() { var rootCommand = Program.CreateRootCommand(); var openApi = Path.Combine(".", "UtilityFiles", "SampleOpenApi.yml"); @@ -298,14 +338,14 @@ public void InvokeShowCommand() var handler = rootCommand.Subcommands.Where(c => c.Name == "show").First().Handler; var context = new InvocationContext(parseResult); - handler!.Invoke(context); + await handler!.InvokeAsync(context); - var output = File.ReadAllText("sample.md"); + var output = await File.ReadAllTextAsync("sample.md"); Assert.Contains("graph LR", output, StringComparison.Ordinal); } [Fact] - public void InvokePluginCommand() + public async Task InvokePluginCommandAsync() { var rootCommand = Program.CreateRootCommand(); var manifest = Path.Combine(".", "UtilityFiles", "exampleapimanifest.json"); @@ -314,9 +354,9 @@ public void InvokePluginCommand() var handler = rootCommand.Subcommands.Where(c => c.Name == "plugin").First().Handler; var context = new InvocationContext(parseResult); - handler!.Invoke(context); + await handler!.InvokeAsync(context); - using var jsDoc = JsonDocument.Parse(File.ReadAllText("ai-plugin.json")); + using var jsDoc = JsonDocument.Parse(await File.ReadAllTextAsync("ai-plugin.json")); var openAiManifest = OpenAIPluginManifest.Load(jsDoc.RootElement); Assert.NotNull(openAiManifest); diff --git a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml new file mode 100644 index 000000000..3260ea430 --- /dev/null +++ b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml @@ -0,0 +1,81 @@ +openapi: 3.0.1 +info: + title: Example with Multiple Operations and Local $refs + version: 1.0.0 +servers: +- url: https://api.github.com +paths: + /items: + get: + operationId: getItems + summary: Get a list of items + responses: + '200': + description: A list of items + headers: + x-custom-header: + $ref: '#/components/headers/CustomHeader' + content: + application/json: + schema: + type: array + items: + type: string + examples: + ItemExample: + $ref: '#/components/examples/ItemExample' + post: + operationId: createItem + summary: Create a new item + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: + $ref: '#/components/examples/ItemExample' + responses: + '201': + description: Item created + content: + application/json: + schema: + type: object + properties: + id: + type: string + name: + type: string + example: + $ref: '#/components/examples/ItemExample' +components: + schemas: + pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + headers: + CustomHeader: + description: Custom header for authentication + required: true + schema: + type: string + examples: + ItemExample: + summary: Example of a new item to be created + value: + name: "New Item" + diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index b169ea016..dbc372c99 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -17,12 +17,14 @@ - - + + - + + + diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiDiagnosticTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiDiagnosticTests.cs index cdc793632..5ecd58071 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiDiagnosticTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiDiagnosticTests.cs @@ -41,7 +41,7 @@ public void DetectedSpecificationVersionShouldBeV3_0() } [Fact] - public async Task DiagnosticReportMergedForExternalReference() + public async Task DiagnosticReportMergedForExternalReferenceAsync() { // Create a reader that will resolve all references var settings = new OpenApiReaderSettings diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs index 8fef41c62..c88c86544 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs @@ -1,7 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.IO; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Reader; @@ -37,7 +39,7 @@ public void StreamShouldNotCloseIfLeaveStreamOpenSettingEqualsTrue() } [Fact] - public async Task StreamShouldNotBeDisposedIfLeaveStreamOpenSettingIsTrue() + public async Task StreamShouldNotBeDisposedIfLeaveStreamOpenSettingIsTrueAsync() { var memoryStream = new MemoryStream(); using var fileStream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml")); @@ -50,5 +52,20 @@ public async Task StreamShouldNotBeDisposedIfLeaveStreamOpenSettingIsTrue() stream.Seek(0, SeekOrigin.Begin); // does not throw an object disposed exception Assert.True(stream.CanRead); } + + [Fact] + public async Task StreamShouldReadWhenInitializedAsync() + { + var httpClient = new HttpClient + { + BaseAddress = new Uri("https://raw.githubusercontent.com/OAI/OpenAPI-Specification/") + }; + + var stream = await httpClient.GetStreamAsync("master/examples/v3.0/petstore.yaml"); + + // Read V3 as YAML + var result = OpenApiDocument.Load(stream, "yaml"); + Assert.NotNull(result.OpenApiDocument); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs index 2ee51bc06..4aca9b54e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading.Tasks; using Microsoft.OpenApi.Interfaces; @@ -20,7 +20,7 @@ public OpenApiWorkspaceStreamTests() // Use OpenApiWorkspace to load a document and a referenced document [Fact] - public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoWorkspace() + public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoWorkspaceAsync() { // Create a reader that will resolve all references var settings = new OpenApiReaderSettings @@ -40,8 +40,8 @@ public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoW paths: {} """; var wr = new StreamWriter(stream); - wr.Write(doc); - wr.Flush(); + await wr.WriteAsync(doc); + await wr.FlushAsync(); stream.Position = 0; var result = await OpenApiDocument.LoadAsync(stream, OpenApiConstants.Yaml, settings: settings); @@ -50,7 +50,7 @@ public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoW } [Fact] - public async Task LoadDocumentWithExternalReferenceShouldLoadExternalDocumentComponentsIntoWorkspace() + public async Task LoadDocumentWithExternalReferenceShouldLoadBothDocumentsIntoWorkspaceAsync() { // Create a reader that will resolve all references var settings = new OpenApiReaderSettings @@ -83,7 +83,7 @@ public Stream Load(Uri uri) public Task LoadAsync(Uri uri) { - return null; + return Task.FromResult(null); } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 1e69c6818..314e22273 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using FluentAssertions; -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; @@ -17,6 +16,7 @@ using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Validations.Rules; using Microsoft.OpenApi.Writers; +using SharpYaml.Model; using Xunit; namespace Microsoft.OpenApi.Readers.Tests.V3Tests @@ -112,7 +112,7 @@ public void ParseDocumentFromInlineStringShouldSucceed() [Fact] public void ParseBasicDocumentWithMultipleServersShouldSucceed() { - var path = Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml"); + var path = System.IO.Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml"); var result = OpenApiDocument.Load(path); result.OpenApiDiagnostic.Should().BeEquivalentTo( @@ -152,7 +152,7 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed() [Fact] public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml")); + using var stream = Resources.GetStream(System.IO.Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml")); var result = OpenApiDocument.Load(stream, OpenApiConstants.Yaml); result.OpenApiDocument.Should().BeEquivalentTo( @@ -180,7 +180,7 @@ public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic() [Fact] public void ParseMinimalDocumentShouldSucceed() { - var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); + var result = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "minimalDocument.yaml")); result.OpenApiDocument.Should().BeEquivalentTo( new OpenApiDocument @@ -207,7 +207,7 @@ public void ParseMinimalDocumentShouldSucceed() [Fact] public void ParseStandardPetStoreDocumentShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml")); + using var stream = Resources.GetStream(System.IO.Path.Combine(SampleFolderPath, "petStore.yaml")); var actual = OpenApiDocument.Load(stream, OpenApiConstants.Yaml); var components = new OpenApiComponents @@ -593,7 +593,7 @@ public void ParseStandardPetStoreDocumentShouldSucceed() [Fact] public void ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStoreWithTagAndSecurity.yaml")); + using var stream = Resources.GetStream(System.IO.Path.Combine(SampleFolderPath, "petStoreWithTagAndSecurity.yaml")); var actual = OpenApiDocument.Load(stream, OpenApiConstants.Yaml); var components = new OpenApiComponents @@ -1105,7 +1105,7 @@ public void ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() [Fact] public void ParsePetStoreExpandedShouldSucceed() { - var actual = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "petStoreExpanded.yaml")); + var actual = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "petStoreExpanded.yaml")); // TODO: Create the object in memory and compare with the one read from YAML file. @@ -1116,7 +1116,7 @@ public void ParsePetStoreExpandedShouldSucceed() [Fact] public void GlobalSecurityRequirementShouldReferenceSecurityScheme() { - var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "securedApi.yaml")); + var result = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "securedApi.yaml")); var securityRequirement = result.OpenApiDocument.SecurityRequirements.First(); @@ -1127,7 +1127,7 @@ public void GlobalSecurityRequirementShouldReferenceSecurityScheme() [Fact] public void HeaderParameterShouldAllowExample() { - var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "apiWithFullHeaderComponent.yaml")); + var result = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "apiWithFullHeaderComponent.yaml")); var exampleHeader = result.OpenApiDocument.Components?.Headers?["example-header"]; Assert.NotNull(exampleHeader); @@ -1195,7 +1195,7 @@ public void ParseDocumentWithReferencedSecuritySchemeWorks() ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences }; - var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "docWithSecuritySchemeReference.yaml"), settings); + var result = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "docWithSecuritySchemeReference.yaml"), settings); var securityScheme = result.OpenApiDocument.Components.SecuritySchemes["OAuth2"]; // Assert @@ -1207,7 +1207,7 @@ public void ParseDocumentWithReferencedSecuritySchemeWorks() public void ParseDocumentWithJsonSchemaReferencesWorks() { // Arrange - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "docWithJsonSchema.yaml")); + using var stream = Resources.GetStream(System.IO.Path.Combine(SampleFolderPath, "docWithJsonSchema.yaml")); // Act var settings = new OpenApiReaderSettings @@ -1227,7 +1227,7 @@ public void ParseDocumentWithJsonSchemaReferencesWorks() public void ValidateExampleShouldNotHaveDataTypeMismatch() { // Act - var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "documentWithDateExampleInSchema.yaml"), new OpenApiReaderSettings + var result = OpenApiDocument.Load(System.IO.Path.Combine(SampleFolderPath, "documentWithDateExampleInSchema.yaml"), new OpenApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences @@ -1327,7 +1327,7 @@ public void ParseDocWithRefsUsingProxyReferencesSucceeds() format: int32 default: 10"; - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minifiedPetStore.yaml")); + using var stream = Resources.GetStream(System.IO.Path.Combine(SampleFolderPath, "minifiedPetStore.yaml")); // Act var doc = OpenApiDocument.Load(stream, "yaml").OpenApiDocument; @@ -1344,5 +1344,78 @@ public void ParseDocWithRefsUsingProxyReferencesSucceeds() .IgnoringCyclicReferences()); outputDoc.Should().BeEquivalentTo(expectedSerializedDoc.MakeLineBreaksEnvironmentNeutral()); } + + [Fact] + public void ParseBasicDocumentWithServerVariableShouldSucceed() + { + var result = OpenApiDocument.Parse(""" + openapi : 3.0.0 + info: + title: The API + version: 0.9.1 + servers: + - url: http://www.example.org/api/{version} + description: The http endpoint + variables: + version: + default: v2 + enum: [v1, v2] + paths: {} + """, "yaml"); + + var expected = new OpenApiDocument + { + Info = new() + { + Title = "The API", + Version = "0.9.1", + }, + Servers = + { + new OpenApiServer + { + Url = "http://www.example.org/api/{version}", + Description = "The http endpoint", + Variables = new Dictionary + { + {"version", new OpenApiServerVariable {Default = "v2", Enum = ["v1", "v2"]}} + } + } + }, + Paths = new() + }; + + result.OpenApiDiagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic + { + SpecificationVersion = OpenApiSpecVersion.OpenApi3_0, + Errors = new List() + { + new OpenApiError("", "Paths is a REQUIRED field at #/") + } + }); + + result.OpenApiDocument.Should().BeEquivalentTo(expected, options => options.Excluding(x => x.BaseUri)); + } + + [Fact] + public void ParseBasicDocumentWithServerVariableAndNoDefaultShouldFail() + { + var result = OpenApiDocument.Parse(""" + openapi : 3.0.0 + info: + title: The API + version: 0.9.1 + servers: + - url: http://www.example.org/api/{version} + description: The http endpoint + variables: + version: + enum: [v1, v2] + paths: {} + """, "yaml"); + + result.OpenApiDiagnostic.Errors.Should().NotBeEmpty(); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiMediaTypeTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiMediaTypeTests.cs index f7102b338..2c368cc22 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiMediaTypeTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiMediaTypeTests.cs @@ -3,9 +3,12 @@ using System.IO; using FluentAssertions; -using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Reader; +using Microsoft.OpenApi.Reader.ParseNodes; +using Microsoft.OpenApi.Reader.V3; +using Microsoft.OpenApi.Tests; using Xunit; namespace Microsoft.OpenApi.Readers.Tests.V3Tests @@ -71,5 +74,43 @@ public void ParseMediaTypeWithExamplesShouldSucceed() .Excluding(m => m.Examples["example1"].Value.Parent) .Excluding(m => m.Examples["example2"].Value.Parent)); } + + [Fact] + public void ParseMediaTypeWithEmptyArrayInExamplesWorks() + { + // Arrange + var expected = @"{ + ""schema"": { + ""type"": ""array"", + ""items"": { + ""type"": ""object"", + ""properties"": { + ""id"": { + ""type"": ""string"" + } + } + } + }, + ""examples"": { + ""Success response - no results"": { + ""value"": [ ] + } + } +} +"; + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "examplesWithEmptyArray.json"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var mediaType = OpenApiV3Deserializer.LoadMediaType(node); + var serialized = mediaType.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + serialized.MakeLineBreaksEnvironmentNeutral() + .Should().BeEquivalentTo(expected.MakeLineBreaksEnvironmentNeutral()); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiMediaType/examplesWithEmptyArray.json b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiMediaType/examplesWithEmptyArray.json new file mode 100644 index 000000000..0d13dcaf2 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiMediaType/examplesWithEmptyArray.json @@ -0,0 +1,18 @@ +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + }, + "examples": { + "Success response - no results": { + "value": [] + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs b/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs deleted file mode 100644 index eb8366817..000000000 --- a/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Reader; -using Newtonsoft.Json.Linq; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.OpenApi.SmokeTests -{ - [Collection("DefaultSettings")] - public class ApisGuruTests - { - private static HttpClient _httpClient; - private readonly ITestOutputHelper _output; - - public ApisGuruTests(ITestOutputHelper output) - { - _output = output; - } - - static ApisGuruTests() - { - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - _httpClient = new(new HttpClientHandler - { - AutomaticDecompression = DecompressionMethods.GZip - }); - _httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new("gzip")); - _httpClient.DefaultRequestHeaders.UserAgent.Add(new("OpenApi.Net.Tests", "1.0")); - } - - public static IEnumerable GetSchemas() - { - var listJsonStr = _httpClient - .GetStringAsync("https://api.apis.guru/v2/list.json") - .GetAwaiter().GetResult(); - - var json = JObject.Parse(listJsonStr); - foreach (var item in json.Properties()) - { - if (GetProp(item.Value, "versions") is not JObject versions) - continue; - foreach (var prop in versions.Properties()) - { - var urlToJson = GetProp(prop.Value, "swaggerUrl")?.ToObject(); - if (urlToJson != null) - yield return new object[] { urlToJson }; - - var utlToYaml = GetProp(prop.Value, "swaggerYamlUrl")?.ToObject(); - if (utlToYaml != null) - yield return new object[] { utlToYaml }; - } - } - - JToken GetProp(JToken obj, string prop) - { - if (obj is not JObject jObj) - return null; - if (!jObj.TryGetValue(prop, out var jToken)) - return null; - return jToken; - } - } - - // Disable as some APIs are currently invalid - //[Theory(DisplayName = "APIs.guru")] - //[MemberData(nameof(GetSchemas))] - public async Task EnsureThatICouldParse(string url) - { - var response = await _httpClient.GetAsync(url); - if (!response.IsSuccessStatusCode) - { - _output.WriteLine($"Couldn't load {url}"); - return; - } - - await response.Content.LoadIntoBufferAsync(); - var stream = await response.Content.ReadAsStreamAsync(); - - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - var format = OpenApiModelFactory.GetFormat(url); - var result = OpenApiDocument.Load(stream, format); - - if (result.OpenApiDiagnostic.Errors.Count > 0) - { - _output.WriteLine($"Errors parsing {url}"); - _output.WriteLine(String.Join('\n', result.OpenApiDiagnostic.Errors)); - // Assert.True(false); // Uncomment to identify descriptions with errors. - } - - Assert.NotNull(result.OpenApiDocument); - stopwatch.Stop(); - _output.WriteLine($"Parsing {url} took {stopwatch.ElapsedMilliseconds} ms."); - } - } -} diff --git a/test/Microsoft.OpenApi.SmokeTests/GraphTests.cs b/test/Microsoft.OpenApi.SmokeTests/GraphTests.cs deleted file mode 100644 index 4527f1016..000000000 --- a/test/Microsoft.OpenApi.SmokeTests/GraphTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Services; -using System; -using System.Net; -using System.Net.Http; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.OpenApi.SmokeTests -{ - public class GraphTests - { - OpenApiDocument _graphOpenApi; - HttpClient _httpClient; - private readonly ITestOutputHelper _output; - const string graphOpenApiUrl = "https://github.com/microsoftgraph/microsoft-graph-openapi/blob/master/v1.0.json?raw=true"; - - public GraphTests(ITestOutputHelper output) - { - _output = output; - System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - _httpClient = new HttpClient(new HttpClientHandler() - { - AutomaticDecompression = DecompressionMethods.GZip - }); - _httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new("gzip")); - _httpClient.DefaultRequestHeaders.UserAgent.Add(new("OpenApi.Net.Tests", "1.0")); - - var response = _httpClient.GetAsync(graphOpenApiUrl) - .GetAwaiter().GetResult(); - - if (!response.IsSuccessStatusCode) - { - _output.WriteLine($"Couldn't load graph openapi"); - return; - } - - var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); ; - - var result = OpenApiDocument.Load(stream, "json"); - _graphOpenApi = result.OpenApiDocument; - - if (result.OpenApiDiagnostic.Errors.Count > 0) - { - _output.WriteLine($"Errors parsing"); - _output.WriteLine(String.Join('\n', result.OpenApiDiagnostic.Errors)); - // Assert.True(false); // Uncomment to identify descriptions with errors. - } - } - - //[Fact(Skip="Run manually")] - public void LoadOpen() - { - var operations = new[] { "foo", "bar" }; - var subset = new OpenApiDocument(); - Assert.NotNull(_graphOpenApi); - } - } -} diff --git a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj deleted file mode 100644 index 8954143d6..000000000 --- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net8.0 - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/Microsoft.OpenApi.SmokeTests/WorkspaceTests.cs b/test/Microsoft.OpenApi.SmokeTests/WorkspaceTests.cs deleted file mode 100644 index 0d0056fe3..000000000 --- a/test/Microsoft.OpenApi.SmokeTests/WorkspaceTests.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Microsoft.OpenApi.SmokeTests -{ - public class WorkspaceTests - { - } -} diff --git a/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs b/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs index e0e664335..182108260 100644 --- a/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs @@ -1,4 +1,5 @@ -using Microsoft.OpenApi.Attributes; +using System; +using Microsoft.OpenApi.Attributes; using Microsoft.OpenApi.Extensions; using Xunit; @@ -14,6 +15,19 @@ public enum ApiLevel Corporate = 3 } + [Flags] + public enum UserType + { + [DisplayAttribute("admin")] + Admin = 1, + [DisplayAttribute("editor")] + Editor = 2, + [DisplayAttribute("publisher")] + Publisher = 3, + [DisplayAttribute("all")] + All = Admin | Editor | Publisher + } + public class DisplayAttributeTests { [Theory] @@ -24,5 +38,23 @@ public void GetDisplayNameExtensionShouldUseDisplayAttribute(ApiLevel apiLevel, { Assert.Equal(expected, apiLevel.GetDisplayName()); } + + [Theory] + [InlineData(ApiLevel.Private,"private")] + [InlineData(ApiLevel.Public, "public")] + [InlineData(ApiLevel.Corporate, "corporate")] + public void GetEnumFromDisplayNameShouldReturnEnumValue(ApiLevel expected, string displayName) + { + Assert.Equal(expected, displayName.GetEnumFromDisplayName()); + } + + [Theory] + [InlineData(UserType.Admin,"admin")] + [InlineData(UserType.Publisher, "publisher")] + [InlineData(UserType.Editor, "editor")] + public void GetEnumFromDisplayNameShouldReturnEnumValueForFlagsEnum(UserType expected, string displayName) + { + Assert.Equal(expected, displayName.GetEnumFromDisplayName()); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Extensions/OpenApiServerExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Extensions/OpenApiServerExtensionsTests.cs new file mode 100644 index 000000000..b8f581541 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Extensions/OpenApiServerExtensionsTests.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Models; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Extensions; + +public class OpenApiServerExtensionsTests +{ + [Fact] + public void ShouldSubstituteServerVariableWithProvidedValues() + { + var variable = new OpenApiServer + { + Url = "http://example.com/api/{version}", + Description = string.Empty, + Variables = new Dictionary + { + { "version", new OpenApiServerVariable { Default = "v1", Enum = ["v1", "v2"]} } + } + }; + + var url = variable.ReplaceServerUrlVariables(new Dictionary {{"version", "v2"}}); + + url.Should().Be("http://example.com/api/v2"); + } + + [Fact] + public void ShouldSubstituteServerVariableWithDefaultValues() + { + var variable = new OpenApiServer + { + Url = "http://example.com/api/{version}", + Description = string.Empty, + Variables = new Dictionary + { + { "version", new OpenApiServerVariable { Default = "v1", Enum = ["v1", "v2"]} } + } + }; + + var url = variable.ReplaceServerUrlVariables(new Dictionary(0)); + + url.Should().Be("http://example.com/api/v1"); + } + + [Fact] + public void ShouldFailIfNoValueIsAvailable() + { + var variable = new OpenApiServer + { + Url = "http://example.com/api/{version}", + Description = string.Empty, + Variables = new Dictionary + { + { "version", new OpenApiServerVariable { Enum = ["v1", "v2"]} } + } + }; + + Assert.Throws(() => + { + variable.ReplaceServerUrlVariables(new Dictionary(0)); + }); + } + + [Fact] + public void ShouldFailIfProvidedValueIsNotInEnum() + { + var variable = new OpenApiServer + { + Url = "http://example.com/api/{version}", + Description = string.Empty, + Variables = new Dictionary + { + { "version", new OpenApiServerVariable { Enum = ["v1", "v2"]} } + } + }; + + Assert.Throws(() => + { + variable.ReplaceServerUrlVariables(new Dictionary {{"version", "v3"}}); + }); + } + + [Fact] + public void ShouldFailIfEnumIsEmpty() + { + var variable = new OpenApiServer + { + Url = "http://example.com/api/{version}", + Description = string.Empty, + Variables = new Dictionary + { + { "version", new OpenApiServerVariable { Enum = []} } + } + }; + + Assert.Throws(() => + { + variable.ReplaceServerUrlVariables(new Dictionary {{"version", "v1"}}); + }); + } +} diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index e1f54a276..3aff8aa0d 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -10,13 +10,13 @@ - - - + + + - - + + diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 98% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 8017028d1..36135796e 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "$request.body#/url": { "post": { "requestBody": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..586f0ed17 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$request.body#/url":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Success"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 690cc5e9d..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeAdvancedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$request.body#/url":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Success"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt similarity index 98% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 8017028d1..36135796e 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "$request.body#/url": { "post": { "requestBody": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..586f0ed17 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$request.body#/url":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Success"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 690cc5e9d..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$request.body#/url":{"post":{"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Success"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 90% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 8c9f1f140..81907fcb2 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/callbacks/simpleHook" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..5d2495706 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$ref":"#/components/callbacks/simpleHook"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 20e44f987..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.SerializeReferencedCallbackAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$ref":"#/components/callbacks/simpleHook"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs index c871c50c3..61485897a 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs @@ -97,7 +97,7 @@ public class OpenApiCallbackTests [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedCallbackAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedCallbackAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -114,7 +114,7 @@ public async Task SerializeAdvancedCallbackAsV3JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedCallbackAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeReferencedCallbackAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -131,7 +131,7 @@ public async Task SerializeReferencedCallbackAsV3JsonWorks(bool produceTerseOutp [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedCallbackAsV3JsonWithoutReferenceWorks(bool produceTerseOutput) + public async Task SerializeReferencedCallbackAsV3JsonWithoutReferenceWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 99% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 46c5b2e30..09804c666 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "swagger": "2.0", "info": { "title": "Swagger Petstore (Simple)", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..0cd09732e --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 100% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt similarity index 100% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=True.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 72106e400..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","parameters":[{"name":"tags","in":"query","description":"tags to filter by","schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","description":"maximum number of results to return","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"application/xml":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","requestBody":{"description":"Pet to add to the store","content":{"application/json":{"schema":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","parameters":[{"name":"id","in":"path","description":"ID of pet to fetch","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"application/xml":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","parameters":[{"name":"id","in":"path","description":"ID of pet to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 99% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 46c5b2e30..09804c666 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "swagger": "2.0", "info": { "title": "Swagger Petstore (Simple)", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..0cd09732e --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 99% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index a94db37b7..49c47bc62 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -30,6 +30,7 @@ "name": "tags", "in": "query", "description": "tags to filter by", + "style": "form", "schema": { "type": "array", "items": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..81beb028b --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","parameters":[{"name":"tags","in":"query","description":"tags to filter by","style":"form","schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","description":"maximum number of results to return","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"application/xml":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","requestBody":{"description":"Pet to add to the store","content":{"application/json":{"schema":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","parameters":[{"name":"id","in":"path","description":"ID of pet to fetch","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"application/xml":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","parameters":[{"name":"id","in":"path","description":"ID of pet to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt deleted file mode 100644 index a688f8525..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ /dev/null @@ -1,495 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Swagger Petstore (Simple)", - "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", - "termsOfService": "http://helloreverb.com/terms/", - "contact": { - "name": "Swagger API team", - "url": "http://swagger.io", - "email": "foo@example.com" - }, - "license": { - "name": "MIT", - "url": "http://opensource.org/licenses/MIT" - }, - "version": "1.0.0" - }, - "servers": [ - { - "url": "http://petstore.swagger.io/api" - } - ], - "paths": { - "/pets": { - "get": { - "description": "Returns all pets from the system that the user has access to", - "operationId": "findPets", - "parameters": [ - { - "name": "tags", - "in": "query", - "description": "tags to filter by", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "limit", - "in": "query", - "description": "maximum number of results to return", - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "pet response", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - } - } - } - }, - "4XX": { - "description": "unexpected client error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - }, - "5XX": { - "description": "unexpected server error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - } - } - }, - "post": { - "description": "Creates a new pet in the store. Duplicates are allowed", - "operationId": "addPet", - "requestBody": { - "description": "Pet to add to the store", - "content": { - "application/json": { - "schema": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "pet response", - "content": { - "application/json": { - "schema": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - } - } - }, - "4XX": { - "description": "unexpected client error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - }, - "5XX": { - "description": "unexpected server error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - } - } - } - }, - "/pets/{id}": { - "get": { - "description": "Returns a user based on a single ID, if the user does not have access to the pet", - "operationId": "findPetById", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "ID of pet to fetch", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "200": { - "description": "pet response", - "content": { - "application/json": { - "schema": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - }, - "application/xml": { - "schema": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - } - } - } - }, - "4XX": { - "description": "unexpected client error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - }, - "5XX": { - "description": "unexpected server error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - } - } - }, - "delete": { - "description": "deletes a single pet based on the ID supplied", - "operationId": "deletePet", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "ID of pet to delete", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "204": { - "description": "pet deleted" - }, - "4XX": { - "description": "unexpected client error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - }, - "5XX": { - "description": "unexpected server error", - "content": { - "text/html": { - "schema": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "pet": { - "required": [ - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "newPet": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "errorModel": { - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } -} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 89% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 1656b2bf7..ae6572f21 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -55,15 +55,15 @@ "schema": { "type": "array", "items": { + "type": "object", "required": [ "id", "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -78,15 +78,15 @@ "4XX": { "description": "unexpected client error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -97,15 +97,15 @@ "5XX": { "description": "unexpected server error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -132,14 +132,14 @@ "description": "Pet to add to the store", "required": true, "schema": { + "type": "object", "required": [ "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -155,15 +155,15 @@ "200": { "description": "pet response", "schema": { + "type": "object", "required": [ "id", "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -177,15 +177,15 @@ "4XX": { "description": "unexpected client error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -196,15 +196,15 @@ "5XX": { "description": "unexpected server error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -238,15 +238,15 @@ "200": { "description": "pet response", "schema": { + "type": "object", "required": [ "id", "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -260,15 +260,15 @@ "4XX": { "description": "unexpected client error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -279,15 +279,15 @@ "5XX": { "description": "unexpected server error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -320,15 +320,15 @@ "4XX": { "description": "unexpected client error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -339,15 +339,15 @@ "5XX": { "description": "unexpected server error", "schema": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" @@ -361,15 +361,15 @@ }, "definitions": { "pet": { + "type": "object", "required": [ "id", "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -380,14 +380,14 @@ } }, "newPet": { + "type": "object", "required": [ "name" ], - "type": "object", "properties": { "id": { - "format": "int64", - "type": "integer" + "type": "integer", + "format": "int64" }, "name": { "type": "string" @@ -398,15 +398,15 @@ } }, "errorModel": { + "type": "object", "required": [ "code", "message" ], - "type": "object", "properties": { "code": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "message": { "type": "string" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt similarity index 52% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=True.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 3670fba11..5ae9e05e5 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"your-resource-name.openai.azure.com","basePath":"/openai","schemes":["https"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}} \ No newline at end of file +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"your-resource-name.openai.azure.com","basePath":"/openai","schemes":["https"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 63% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 52c6a3734..1ba9050ca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "swagger": "2.0", "info": { "title": "Swagger Petstore (Simple)", @@ -64,5 +64,60 @@ } } } + }, + "definitions": { + "pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "newPet": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "errorModel": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..b61ba4d5a --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/add/{operand1}/{operand2}":{"get":{"operationId":"addByOperand1AndByOperand2","produces":["application/json"],"parameters":[{"in":"path","name":"operand1","description":"The first operand","required":true,"type":"integer","my-extension":4},{"in":"path","name":"operand2","description":"The second operand","required":true,"type":"integer","my-extension":4}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}}}},"definitions":{"pet":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"type":"object","required":["name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 64% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index c2e9f5312..c4a235055 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "openapi": "3.0.1", "info": { "title": "Swagger Petstore (Simple)", @@ -71,5 +71,62 @@ } } } + }, + "components": { + "schemas": { + "pet": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "newPet": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "errorModel": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..dc50aeb17 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/add/{operand1}/{operand2}":{"get":{"operationId":"addByOperand1AndByOperand2","parameters":[{"name":"operand1","in":"path","description":"The first operand","required":true,"schema":{"type":"integer","my-extension":4},"my-extension":4},{"name":"operand2","in":"path","description":"The second operand","required":true,"schema":{"type":"integer","my-extension":4},"my-extension":4}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index da61a8817..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/add/{operand1}/{operand2}":{"get":{"operationId":"addByOperand1AndByOperand2","parameters":[{"name":"operand1","in":"path","description":"The first operand","required":true,"schema":{"type":"integer","my-extension":4},"my-extension":4},{"name":"operand2","in":"path","description":"The second operand","required":true,"schema":{"type":"integer","my-extension":4},"my-extension":4}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index 56dc228e0..fa7a2048f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -43,6 +43,7 @@ public OpenApiDocumentTests() ["property1"] = new() { Type = "string", + Annotations = new Dictionary { { "key1", "value" } } } } }, @@ -61,8 +62,10 @@ public OpenApiDocumentTests() ["property1"] = new() { Type = "string", + Annotations = new Dictionary { { "key1", "value" } } } }, + Annotations = new Dictionary { { "key1", "value" } }, Reference = new() { Type = ReferenceType.Schema, @@ -105,6 +108,7 @@ public OpenApiDocumentTests() { Version = "1.0.0" }, + Annotations = new Dictionary { { "key1", "value" } }, Components = TopLevelReferencingComponents }; @@ -114,6 +118,7 @@ public OpenApiDocumentTests() { Version = "1.0.0" }, + Annotations = new Dictionary { { "key1", "value" } }, Components = TopLevelSelfReferencingComponentsWithOtherProperties }; @@ -123,6 +128,7 @@ public OpenApiDocumentTests() { Version = "1.0.0" }, + Annotations = new Dictionary { { "key1", "value" } }, Components = TopLevelSelfReferencingComponents }; @@ -499,6 +505,7 @@ public OpenApiDocumentTests() } } }, + Annotations = new Dictionary { { "key1", "value" } }, Components = AdvancedComponentsWithReference }; @@ -874,6 +881,7 @@ public OpenApiDocumentTests() } } }, + Annotations = new Dictionary { { "key1", "value" } }, Components = AdvancedComponents }; @@ -1031,13 +1039,315 @@ public OpenApiDocumentTests() } } } - } + }, + Annotations = new Dictionary { { "key1", "value" } }, + Components = AdvancedComponents + }; + + public OpenApiDocument AdvancedDocumentWithServerVariable = new() + { + Info = new() + { + Version = "1.0.0", + Title = "Swagger Petstore (Simple)", + Description = + "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + TermsOfService = new("http://helloreverb.com/terms/"), + Contact = new() + { + Name = "Swagger API team", + Email = "foo@example.com", + Url = new("http://swagger.io") + }, + License = new() + { + Name = "MIT", + Url = new("http://opensource.org/licenses/MIT") + } + }, + Servers = new List + { + new() + { + Url = "https://{endpoint}/openai", + Variables = new Dictionary + { + ["endpoint"] = new() + { + Default = "your-resource-name.openai.azure.com" + } + } + } + }, + Paths = new() + { + ["/pets"] = new() + { + Operations = new Dictionary + { + [OperationType.Get] = new() + { + Description = "Returns all pets from the system that the user has access to", + OperationId = "findPets", + Parameters = new List + { + new() + { + Name = "tags", + In = ParameterLocation.Query, + Description = "tags to filter by", + Required = false, + Schema = new() + { + Type = "array", + Items = new() + { + Type = "string" + } + } + }, + new() + { + Name = "limit", + In = ParameterLocation.Query, + Description = "maximum number of results to return", + Required = false, + Schema = new() + { + Type = "integer", + Format = "int32" + } + } + }, + Responses = new() + { + ["200"] = new() + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new() + { + Schema = new() + { + Type = "array", + Items = PetSchema + } + }, + ["application/xml"] = new() + { + Schema = new() + { + Type = "array", + Items = PetSchema + } + } + } + }, + ["4XX"] = new() + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new() + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + } + } + }, + [OperationType.Post] = new() + { + Description = "Creates a new pet in the store. Duplicates are allowed", + OperationId = "addPet", + RequestBody = new() + { + Description = "Pet to add to the store", + Required = true, + Content = new Dictionary + { + ["application/json"] = new() + { + Schema = NewPetSchema + } + } + }, + Responses = new() + { + ["200"] = new() + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new() + { + Schema = PetSchema + }, + } + }, + ["4XX"] = new() + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new() + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + } + } + } + } + }, + ["/pets/{id}"] = new() + { + Operations = new Dictionary + { + [OperationType.Get] = new() + { + Description = + "Returns a user based on a single ID, if the user does not have access to the pet", + OperationId = "findPetById", + Parameters = new List + { + new() + { + Name = "id", + In = ParameterLocation.Path, + Description = "ID of pet to fetch", + Required = true, + Schema = new() + { + Type = "integer", + Format = "int64" + } + } + }, + Responses = new() + { + ["200"] = new() + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new() + { + Schema = PetSchema + }, + ["application/xml"] = new() + { + Schema = PetSchema + } + } + }, + ["4XX"] = new() + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new() + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + } + } + }, + [OperationType.Delete] = new() + { + Description = "deletes a single pet based on the ID supplied", + OperationId = "deletePet", + Parameters = new List + { + new() + { + Name = "id", + In = ParameterLocation.Path, + Description = "ID of pet to delete", + Required = true, + Schema = new() + { + Type = "integer", + Format = "int64" + } + } + }, + Responses = new() + { + ["204"] = new() + { + Description = "pet deleted" + }, + ["4XX"] = new() + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new() + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new() + { + Schema = ErrorModelSchema + } + } + } + } + } + } + } + }, + Annotations = new Dictionary { { "key1", "value" } }, + Components = AdvancedComponents }; [Theory] [InlineData(false)] [InlineData(true)] - public async Task SerializeAdvancedDocumentAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedDocumentAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1054,7 +1364,7 @@ public async Task SerializeAdvancedDocumentAsV3JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedDocumentWithReferenceAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedDocumentWithReferenceAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1071,7 +1381,24 @@ public async Task SerializeAdvancedDocumentWithReferenceAsV3JsonWorks(bool produ [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedDocumentAsV2JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedDocumentWithServerVariableAsV2JsonWorksAsync(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); + + // Act + AdvancedDocumentWithServerVariable.SerializeAsV2(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeAdvancedDocumentAsV2JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1088,7 +1415,7 @@ public async Task SerializeAdvancedDocumentAsV2JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeDuplicateExtensionsAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeDuplicateExtensionsAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1105,7 +1432,7 @@ public async Task SerializeDuplicateExtensionsAsV3JsonWorks(bool produceTerseOut [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeDuplicateExtensionsAsV2JsonWorks(bool produceTerseOutput) + public async Task SerializeDuplicateExtensionsAsV2JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1122,7 +1449,7 @@ public async Task SerializeDuplicateExtensionsAsV2JsonWorks(bool produceTerseOut [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedDocumentWithReferenceAsV2JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedDocumentWithReferenceAsV2JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -1526,6 +1853,60 @@ public void SerializeV2DocumentWithStyleAsNullDoesNotWriteOutStyleValue() actual.Should().Be(expected); } + [Fact] + public void OpenApiDocumentCopyConstructorWithAnnotationsSucceeds() + { + var baseDocument = new OpenApiDocument + { + Annotations = new Dictionary + { + ["key1"] = "value1", + ["key2"] = 2 + } + }; + + var actualDocument = new OpenApiDocument(baseDocument); + + Assert.Equal(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]); + + baseDocument.Annotations["key1"] = "value2"; + + Assert.NotEqual(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]); + } + + [Fact] + public void SerializeExamplesDoesNotThrowNullReferenceException() + { + OpenApiDocument doc = new OpenApiDocument + { + Paths = new OpenApiPaths + { + ["test"] = new OpenApiPathItem() + { + Operations = new Dictionary() + { + [OperationType.Post] = new OpenApiOperation + { + RequestBody = new OpenApiRequestBody() + { + Content = + { + ["application/json"] = new OpenApiMediaType() + { + Examples = null, + }, + } + } + }, + } + }, + } + }; + + OpenApiJsonWriter apiWriter = new OpenApiJsonWriter(new StringWriter()); + doc.Invoking(d => d.SerializeAsV3(apiWriter)).Should().NotThrow(); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 99% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 3238e0274..a9950245b 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "value": { "versions": [ { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..4dfb0ce93 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeAdvancedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"value":{"versions":[{"status":"Status1","id":"v1","links":[{"href":"http://example.com/1","rel":"sampleRel1","bytes":"\"AQID\"","binary":"Ñ😻😑♮Í☛oƞ♑😲☇éNjžŁ♻😟¥a´Ī♃ƠąøƩ"}]},{"status":"Status2","id":"v2","links":[{"href":"http://example.com/2","rel":"sampleRel2"}]}]}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt similarity index 98% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 42c25d91b..6282fd236 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "value": { "versions": [ { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..c319c88f1 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"value":{"versions":[{"status":"Status1","id":"v1","links":[{"href":"http://example.com/1","rel":"sampleRel1"}]},{"status":"Status2","id":"v2","links":[{"href":"http://example.com/2","rel":"sampleRel2"}]}],"aDate":"\"2022-12-12\""}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 89% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 74aae72ef..852a9bc0b 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/examples/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..41c47dfbc --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$ref":"#/components/examples/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 12898c9c5..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$ref":"#/components/examples/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs index 266761f70..023f4d78f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs @@ -100,7 +100,7 @@ public class OpenApiExampleTests [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedExampleAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedExampleAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -117,7 +117,7 @@ public async Task SerializeAdvancedExampleAsV3JsonWorks(bool produceTerseOutput) [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedExampleAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeReferencedExampleAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -134,7 +134,7 @@ public async Task SerializeReferencedExampleAsV3JsonWorks(bool produceTerseOutpu [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedExampleAsV3JsonWithoutReferenceWorks(bool produceTerseOutput) + public async Task SerializeReferencedExampleAsV3JsonWithoutReferenceWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 93% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 5b0eb86be..112b8c37f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "sampleHeader", "type": "integer", "format": "int32" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..63c989200 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"description":"sampleHeader","type":"integer","format":"int32"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 8feb99289..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"description":"sampleHeader","type":"integer","format":"int32"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 95% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 8234610e0..6356b0527 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "sampleHeader", "schema": { "type": "integer", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..7d0a160ce --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"description":"sampleHeader","schema":{"type":"integer","format":"int32"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 37ebf2515..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"description":"sampleHeader","schema":{"type":"integer","format":"int32"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt similarity index 93% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 5b0eb86be..112b8c37f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeAdvancedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "sampleHeader", "type": "integer", "format": "int32" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..63c989200 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"description":"sampleHeader","type":"integer","format":"int32"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 8feb99289..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"description":"sampleHeader","type":"integer","format":"int32"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 86% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 9791d3c4a..3c5b532f0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/headers/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..4e1ff3605 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$ref":"#/headers/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 58060ead9..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV2JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$ref":"#/headers/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt similarity index 95% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 8234610e0..6356b0527 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "sampleHeader", "schema": { "type": "integer", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..7d0a160ce --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"description":"sampleHeader","schema":{"type":"integer","format":"int32"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 37ebf2515..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"description":"sampleHeader","schema":{"type":"integer","format":"int32"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 89% rename from test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 18045b9d2..5b44f6427 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/headers/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..fc4d9c306 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$ref":"#/components/headers/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index 8c4124b8d..000000000 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.SerializeReferencedHeaderAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"$ref":"#/components/headers/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs index 014092e93..9eded3dea 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs @@ -40,7 +40,7 @@ public class OpenApiHeaderTests [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedHeaderAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedHeaderAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -57,7 +57,7 @@ public async Task SerializeAdvancedHeaderAsV3JsonWorks(bool produceTerseOutput) [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedHeaderAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeReferencedHeaderAsV3JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -74,7 +74,7 @@ public async Task SerializeReferencedHeaderAsV3JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks(bool produceTerseOutput) + public async Task SerializeReferencedHeaderAsV3JsonWithoutReferenceWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -91,7 +91,7 @@ public async Task SerializeReferencedHeaderAsV3JsonWithoutReferenceWorks(bool pr [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeAdvancedHeaderAsV2JsonWorks(bool produceTerseOutput) + public async Task SerializeAdvancedHeaderAsV2JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -108,7 +108,7 @@ public async Task SerializeAdvancedHeaderAsV2JsonWorks(bool produceTerseOutput) [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedHeaderAsV2JsonWorks(bool produceTerseOutput) + public async Task SerializeReferencedHeaderAsV2JsonWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -125,7 +125,7 @@ public async Task SerializeReferencedHeaderAsV2JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeReferencedHeaderAsV2JsonWithoutReferenceWorks(bool produceTerseOutput) + public async Task SerializeReferencedHeaderAsV2JsonWithoutReferenceWorksAsync(bool produceTerseOutput) { // Arrange var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 2629e0b1c..6beea68d7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "operationId": "operationId1", "parameters": { "parameter1": "$request.path.id" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index c9c1701b5..d517845f1 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeAdvancedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"operationId":"operationId1","parameters":{"parameter1":"$request.path.id"},"requestBody":{"property1":true},"description":"description1","server":{"description":"serverDescription1"}} \ No newline at end of file +{"operationId":"operationId1","parameters":{"parameter1":"$request.path.id"},"requestBody":{"property1":true},"description":"description1","server":{"description":"serverDescription1"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 2629e0b1c..6beea68d7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "operationId": "operationId1", "parameters": { "parameter1": "$request.path.id" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index c9c1701b5..d517845f1 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"operationId":"operationId1","parameters":{"parameter1":"$request.path.id"},"requestBody":{"property1":true},"description":"description1","server":{"description":"serverDescription1"}} \ No newline at end of file +{"operationId":"operationId1","parameters":{"parameter1":"$request.path.id"},"requestBody":{"property1":true},"description":"description1","server":{"description":"serverDescription1"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 26fe6229d..8e0fba2e8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/links/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 2200957a3..eb4e2d9a7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.SerializeReferencedLinkAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/components/links/example1"} \ No newline at end of file +{"$ref":"#/components/links/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs index dc18a1341..53f4d14f7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs @@ -81,7 +81,8 @@ public class OpenApiOperationTests Url = "http://server.com", Description = "serverDescription" } - } + }, + Annotations = new Dictionary { { "key1", "value1" }, { "key2", 2 } }, }; private static readonly OpenApiOperation _advancedOperationWithTagsAndSecurity = new() @@ -830,5 +831,26 @@ public void EnsureOpenApiOperationCopyConstructor_SerializationResultsInSame() actual.Should().Be(expected); } } + + [Fact] + public void OpenApiOperationCopyConstructorWithAnnotationsSucceeds() + { + var baseOperation = new OpenApiOperation + { + Annotations = new Dictionary + { + ["key1"] = "value1", + ["key2"] = 2 + } + }; + + var actualOperation = new OpenApiOperation(baseOperation); + + Assert.Equal(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]); + + baseOperation.Annotations["key1"] = "value2"; + + Assert.NotEqual(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=False.verified.txt index 1c8e22a01..5d3060ec5 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "name": "name1", "in": "query", "description": "description1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=True.verified.txt index 73c77d79f..a4b87db10 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeFalseWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"name":"name1","in":"query","description":"description1","style":"form","explode":false,"schema":{"type":"array","items":{"enum":["value1","value2"]}}} \ No newline at end of file +{"name":"name1","in":"query","description":"description1","style":"form","explode":false,"schema":{"type":"array","items":{"enum":["value1","value2"]}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=False.verified.txt index 651da1cce..36a9c0168 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "name": "name1", "in": "query", "description": "description1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=True.verified.txt index 579671130..206902b7b 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithFormStyleAndExplodeTrueWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"name":"name1","in":"query","description":"description1","style":"form","schema":{"type":"array","items":{"enum":["value1","value2"]}}} \ No newline at end of file +{"name":"name1","in":"query","description":"description1","style":"form","schema":{"type":"array","items":{"enum":["value1","value2"]}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 744f8451c..82c260ad3 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "in": "header", "name": "name1", "description": "description1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 26b158865..d96c5ec61 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file +{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 744f8451c..82c260ad3 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "in": "header", "name": "name1", "description": "description1", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 26b158865..d96c5ec61 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file +{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 4127038e5..2be79d6af 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "in": "path", "name": "name1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 8677f0fad..228537b57 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"in":"path","name":"name1"} \ No newline at end of file +{"in":"path","name":"name1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index a9154d617..e4e3b52c5 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/parameters/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 712d7ee78..2c9c105db 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/parameters/example1"} \ No newline at end of file +{"$ref":"#/parameters/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 5275532e8..b810c2bc0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "name": "name1", "in": "path" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index ec654beb0..9b65c4a88 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"name":"name1","in":"path"} \ No newline at end of file +{"name":"name1","in":"path"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 654239535..43aade4c4 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/parameters/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 3d61cb3f8..dc6cfd29f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/components/parameters/example1"} \ No newline at end of file +{"$ref":"#/components/parameters/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index ccc8d3725..0f8d7659c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "description", "content": { "application/json": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 31161c2f5..767ce7937 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeAdvancedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"description":"description","content":{"application/json":{"schema":{"type":"string"}}},"required":true} \ No newline at end of file +{"description":"description","content":{"application/json":{"schema":{"type":"string"}}},"required":true} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index ccc8d3725..0f8d7659c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "description", "content": { "application/json": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 31161c2f5..767ce7937 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"description":"description","content":{"application/json":{"schema":{"type":"string"}}},"required":true} \ No newline at end of file +{"description":"description","content":{"application/json":{"schema":{"type":"string"}}},"required":true} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index ca9bb966e..aaf06f563 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/requestBodies/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 443812023..557936985 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.SerializeReferencedRequestBodyAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/components/requestBodies/example1"} \ No newline at end of file +{"$ref":"#/components/requestBodies/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index af5ce3ea5..2bc39b459 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "A complex object array response", "schema": { "type": "array", diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index f9a3f9d5f..4917a08a6 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"description":"A complex object array response","schema":{"type":"array","items":{"$ref":"#/definitions/customType"}},"headers":{"X-Rate-Limit-Limit":{"description":"The number of allowed requests in the current period","type":"integer"},"X-Rate-Limit-Reset":{"description":"The number of seconds left in the current period","type":"integer"}}} \ No newline at end of file +{"description":"A complex object array response","schema":{"type":"array","items":{"$ref":"#/definitions/customType"}},"headers":{"X-Rate-Limit-Limit":{"description":"The number of allowed requests in the current period","type":"integer"},"X-Rate-Limit-Reset":{"description":"The number of seconds left in the current period","type":"integer"}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index ea5aa0d40..c349731bf 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/responses/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index b2058cfd8..20e1f9783 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/responses/example1"} \ No newline at end of file +{"$ref":"#/responses/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 55bad289b..864f3daee 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "description": "A complex object array response", "headers": { "X-Rate-Limit-Limit": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 612fbe919..08b78d5bd 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"description":"A complex object array response","headers":{"X-Rate-Limit-Limit":{"description":"The number of allowed requests in the current period","schema":{"type":"integer"}},"X-Rate-Limit-Reset":{"description":"The number of seconds left in the current period","schema":{"type":"integer"}}},"content":{"text/plain":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/customType"}}}}} \ No newline at end of file +{"description":"A complex object array response","headers":{"X-Rate-Limit-Limit":{"description":"The number of allowed requests in the current period","schema":{"type":"integer"}},"X-Rate-Limit-Reset":{"description":"The number of seconds left in the current period","schema":{"type":"integer"}}},"content":{"text/plain":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/customType"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 115ec60a6..2b7824755 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ +{ "$ref": "#/components/responses/example1" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index e65264a36..bbd27ff18 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.SerializeReferencedResponseAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"$ref":"#/components/responses/example1"} \ No newline at end of file +{"$ref":"#/components/responses/example1"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..b431f1607 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -0,0 +1,13 @@ +{ + "title": "title1", + "multipleOf": 3, + "maximum": 42, + "minimum": 10, + "exclusiveMinimum": true, + "type": "integer", + "default": 15, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..d71a5f0a8 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"type":"integer","default":15,"nullable":true,"externalDocs":{"url":"http://example.com/externalDocs"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..b431f1607 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=False.verified.txt @@ -0,0 +1,13 @@ +{ + "title": "title1", + "multipleOf": 3, + "maximum": 42, + "minimum": 10, + "exclusiveMinimum": true, + "type": "integer", + "default": 15, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..d71a5f0a8 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"title":"title1","multipleOf":3,"maximum":42,"minimum":10,"exclusiveMinimum":true,"type":"integer","default":15,"nullable":true,"externalDocs":{"url":"http://example.com/externalDocs"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..e9543ede7 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -0,0 +1,41 @@ +{ + "title": "title1", + "required": [ + "property1" + ], + "properties": { + "property1": { + "required": [ + "property3" + ], + "properties": { + "property2": { + "type": "integer" + }, + "property3": { + "type": "string", + "maxLength": 15 + } + } + }, + "property4": { + "properties": { + "property5": { + "properties": { + "property6": { + "type": "boolean" + } + } + }, + "property7": { + "type": "string", + "minLength": 2 + } + }, + "readOnly": true + } + }, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..9ea88dee8 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"title":"title1","required":["property1"],"properties":{"property1":{"required":["property3"],"properties":{"property2":{"type":"integer"},"property3":{"type":"string","maxLength":15}}},"property4":{"properties":{"property5":{"properties":{"property6":{"type":"boolean"}}},"property7":{"type":"string","minLength":2}},"readOnly":true}},"externalDocs":{"url":"http://example.com/externalDocs"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index b67f64de1..1a19457b4 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -1,108 +1,616 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; -using Microsoft.OpenApi.Models; -using Xunit; +using System.Globalization; +using System.IO; +using System.Text.Json.Nodes; +using System.Threading.Tasks; using FluentAssertions; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Microsoft.OpenApi.Writers; +using VerifyXunit; +using Xunit; namespace Microsoft.OpenApi.Tests.Models { + [Collection("DefaultSettings")] public class OpenApiSchemaTests { - public static OpenApiSchema BasicV31Schema = new() + public static OpenApiSchema BasicSchema = new(); + + public static readonly OpenApiSchema AdvancedSchemaNumber = new() + { + Title = "title1", + MultipleOf = 3, + Maximum = 42, + ExclusiveMinimum = true, + Minimum = 10, + Default = 15, + Type = "integer", + + Nullable = true, + ExternalDocs = new() + { + Url = new("http://example.com/externalDocs") + }, + Annotations = new Dictionary { { "key1", "value1" }, { "key2", 2 } } + }; + + public static readonly OpenApiSchema AdvancedSchemaObject = new() { - Id = "https://example.com/arrays.schema.json", - Schema = "https://json-schema.org/draft/2020-12/schema", - Description = "A representation of a person, company, organization, or place", - Type = "object", + Title = "title1", Properties = new Dictionary { - ["fruits"] = new OpenApiSchema + ["property1"] = new() { - Type = "array", - Items = new OpenApiSchema + Properties = new Dictionary { - Type = "string" - } + ["property2"] = new() + { + Type = "integer" + }, + ["property3"] = new() + { + Type = "string", + MaxLength = 15 + } + }, }, - ["vegetables"] = new OpenApiSchema + ["property4"] = new() { - Type = "array" - } + Properties = new Dictionary + { + ["property5"] = new() + { + Properties = new Dictionary + { + ["property6"] = new() + { + Type = "boolean" + } + } + }, + ["property7"] = new() + { + Type = "string", + MinLength = 2 + } + }, + }, }, - Definitions = new Dictionary + Nullable = true, + ExternalDocs = new() { - ["veggie"] = new OpenApiSchema + Url = new("http://example.com/externalDocs") + } + }; + + public static readonly OpenApiSchema AdvancedSchemaWithAllOf = new() + { + Title = "title1", + AllOf = new List + { + new() { - Type = "object", - Required = new HashSet{ "veggieName", "veggieLike" }, + Title = "title2", Properties = new Dictionary { - ["veggieName"] = new OpenApiSchema + ["property1"] = new() + { + Type = "integer" + }, + ["property2"] = new() { Type = "string", - Description = "The name of the vegetable." + MaxLength = 15 + } + }, + }, + new() + { + Title = "title3", + Properties = new Dictionary + { + ["property3"] = new() + { + Properties = new Dictionary + { + ["property4"] = new() + { + Type = "boolean" + } + } }, - ["veggieLike"] = new OpenApiSchema + ["property5"] = new() { - Type = "boolean", - Description = "Do I like this vegetable?" + Type = "string", + MinLength = 2 } - } - } + }, + Nullable = true + }, + }, + Nullable = true, + ExternalDocs = new() + { + Url = new("http://example.com/externalDocs") + } + }; + + public static readonly OpenApiSchema ReferencedSchema = new() + { + Title = "title1", + MultipleOf = 3, + Maximum = 42, + ExclusiveMinimum = true, + Minimum = 10, + Default = 15, + Type = "integer", + + Nullable = true, + ExternalDocs = new() + { + Url = new("http://example.com/externalDocs") + } + }; + + public static readonly OpenApiSchema AdvancedSchemaWithRequiredPropertiesObject = new() + { + Title = "title1", + Required = new HashSet { "property1" }, + Properties = new Dictionary + { + ["property1"] = new() + { + Required = new HashSet { "property3" }, + Properties = new Dictionary + { + ["property2"] = new() + { + Type = "integer" + }, + ["property3"] = new() + { + Type = "string", + MaxLength = 15, + ReadOnly = true + } + }, + ReadOnly = true, + }, + ["property4"] = new() + { + Properties = new Dictionary + { + ["property5"] = new() + { + Properties = new Dictionary + { + ["property6"] = new() + { + Type = "boolean" + } + } + }, + ["property7"] = new() + { + Type = "string", + MinLength = 2 + } + }, + ReadOnly = true, + }, + }, + Nullable = true, + ExternalDocs = new() + { + Url = new("http://example.com/externalDocs") } }; [Fact] - public void SerializeBasicV31SchemaWorks() + public void SerializeBasicSchemaAsV3JsonWorks() { // Arrange - var expected = @"{ - ""$id"": ""https://example.com/arrays.schema.json"", - ""$schema"": ""https://json-schema.org/draft/2020-12/schema"", - ""$defs"": { - ""veggie"": { - ""required"": [ - ""veggieName"", - ""veggieLike"" - ], - ""type"": ""object"", - ""properties"": { - ""veggieName"": { - ""type"": ""string"", - ""description"": ""The name of the vegetable."" - }, - ""veggieLike"": { - ""type"": ""boolean"", - ""description"": ""Do I like this vegetable?"" + var expected = @"{ }"; + + // Act + var actual = BasicSchema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); } - } - } - }, - ""type"": ""object"", - ""properties"": { - ""fruits"": { - ""type"": ""array"", - ""items"": { - ""type"": ""string"" - } - }, - ""vegetables"": { - ""type"": ""array"" - } - }, - ""description"": ""A representation of a person, company, organization, or place"" -}"; + + [Fact] + public void SerializeAdvancedSchemaNumberAsV3JsonWorks() + { + // Arrange + var expected = + """ + { + "title": "title1", + "multipleOf": 3, + "maximum": 42, + "minimum": 10, + "exclusiveMinimum": true, + "type": "integer", + "default": 15, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } + } + """; + + // Act + var actual = AdvancedSchemaNumber.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Fact] + public void SerializeAdvancedSchemaObjectAsV3JsonWorks() + { + // Arrange + var expected = + """ + { + "title": "title1", + "properties": { + "property1": { + "properties": { + "property2": { + "type": "integer" + }, + "property3": { + "maxLength": 15, + "type": "string" + } + } + }, + "property4": { + "properties": { + "property5": { + "properties": { + "property6": { + "type": "boolean" + } + } + }, + "property7": { + "minLength": 2, + "type": "string" + } + } + } + }, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } + } + """; // Act - var actual = BasicV31Schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_1); + var actual = AdvancedSchemaObject.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); actual.Should().Be(expected); } + + [Fact] + public void SerializeAdvancedSchemaWithAllOfAsV3JsonWorks() + { + // Arrange + var expected = + """ + { + "title": "title1", + "allOf": [ + { + "title": "title2", + "properties": { + "property1": { + "type": "integer" + }, + "property2": { + "maxLength": 15, + "type": "string" + } + } + }, + { + "title": "title3", + "properties": { + "property3": { + "properties": { + "property4": { + "type": "boolean" + } + } + }, + "property5": { + "minLength": 2, + "type": "string" + } + }, + "nullable": true + } + ], + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } + } + """; + + // Act + var actual = AdvancedSchemaWithAllOf.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeReferencedSchemaAsV3WithoutReferenceJsonWorksAsync(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); + + // Act + ReferencedSchema.SerializeAsV3(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeReferencedSchemaAsV3JsonWorksAsync(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); + + // Act + ReferencedSchema.SerializeAsV3(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); + + // Act + AdvancedSchemaWithRequiredPropertiesObject.SerializeAsV2(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + + [Fact] + public void SerializeAsV2ShouldSetFormatPropertyInParentSchemaIfPresentInChildrenSchema() + { + // Arrange + var schema = new OpenApiSchema + { + OneOf = new List + { + new() + { + Type = "number", + Format = "decimal" + }, + new() { Type = "string" }, + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var openApiJsonWriter = new OpenApiJsonWriter(outputStringWriter, new() { Terse = false }); + + // Act + // Serialize as V2 + schema.SerializeAsV2(openApiJsonWriter); + openApiJsonWriter.Flush(); + + var v2Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + + var expectedV2Schema = + """ + { + "format": "decimal", + "allOf": [ + { + "type": "number", + "format": "decimal" + } + ] + } + """.MakeLineBreaksEnvironmentNeutral(); + + // Assert + expectedV2Schema.Should().BeEquivalentTo(v2Schema); + } + + [Fact] + public void OpenApiSchemaCopyConstructorSucceeds() + { + var baseSchema = new OpenApiSchema + { + Type = "string", + Format = "date" + }; + + var actualSchema = new OpenApiSchema(baseSchema) + { + Nullable = true + }; + + Assert.Equal("string", actualSchema.Type); + Assert.Equal("date", actualSchema.Format); + Assert.True(actualSchema.Nullable); + } + + [Fact] + public void OpenApiSchemaCopyConstructorWithAnnotationsSucceeds() + { + var baseSchema = new OpenApiSchema + { + Annotations = new Dictionary + { + ["key1"] = "value1", + ["key2"] = 2 + } + }; + + var actualSchema = new OpenApiSchema(baseSchema); + + Assert.Equal(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]); + + baseSchema.Annotations["key1"] = "value2"; + + Assert.NotEqual(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]); + } + + public static TheoryData SchemaExamples() + { + return new() + { + new JsonArray() { "example" }, + new JsonArray { 0, 1, 2 }, // Represent OpenApiBinary as JsonArray of bytes + true, + JsonValue.Create((byte)42), + JsonValue.Create(new DateTime(2024, 07, 19, 12, 34, 56, DateTimeKind.Utc).ToString("o")), // DateTime object + 42.37, + 42.37f, + 42, + null, + JsonValue.Create("secret"), //Represent OpenApiPassword as string + "example", + }; + } + + [Theory] + [MemberData(nameof(SchemaExamples))] + public void CloningSchemaExamplesWorks(JsonNode example) + { + // Arrange + var schema = new OpenApiSchema + { + Example = example + }; + + // Act && Assert + var schemaCopy = new OpenApiSchema(schema); + + // Act && Assert + schema.Example.Should().BeEquivalentTo(schemaCopy.Example, options => options + .IgnoringCyclicReferences() + .Excluding(x => x.Options)); + } + + [Fact] + public void CloningSchemaExtensionsWorks() + { + // Arrange + var schema = new OpenApiSchema + { + Extensions = + { + { "x-myextension", new OpenApiAny(42) } + } + }; + + // Act && Assert + var schemaCopy = new OpenApiSchema(schema); + Assert.Single(schemaCopy.Extensions); + + // Act && Assert + schemaCopy.Extensions = new Dictionary + { + { "x-myextension" , new OpenApiAny(40) } + }; + Assert.NotEqual(schema.Extensions, schemaCopy.Extensions); + } + + [Fact] + public void OpenApiWalkerVisitsOpenApiSchemaNot() + { + var outerSchema = new OpenApiSchema() + { + Title = "Outer Schema", + Not = new OpenApiSchema() + { + Title = "Inner Schema", + Type = "string", + } + }; + + var document = new OpenApiDocument() + { + Paths = new OpenApiPaths() + { + ["/foo"] = new OpenApiPathItem() + { + Parameters = new[] + { + new OpenApiParameter() + { + Name = "foo", + In = ParameterLocation.Query, + Schema = outerSchema, + } + } + } + } + }; + + // Act + var visitor = new SchemaVisitor(); + var walker = new OpenApiWalker(visitor); + walker.Walk(document); + + // Assert + visitor.Titles.Count.Should().Be(2); + } + } + + internal class SchemaVisitor : OpenApiVisitorBase + { + public List Titles = new(); + + public override void Visit(OpenApiSchema schema) + { + Titles.Add(schema.Title); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..d225bb5da --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -0,0 +1,12 @@ +{ + "scheme1": [ + "scope1", + "scope2", + "scope3" + ], + "scheme2": [ + "scope4", + "scope5" + ], + "scheme3": [ ] +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..fc86e7424 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.SerializeSecurityRequirementAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"scheme1":["scope1","scope2","scope3"],"scheme2":["scope4","scope5"],"scheme3":[ ]} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs index 016938839..1e9e13323 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecurityRequirementTests.cs @@ -3,9 +3,14 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading.Tasks; using FluentAssertions; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Writers; +using VerifyXunit; using Microsoft.OpenApi.Models.References; using Xunit; @@ -81,6 +86,23 @@ public void SerializeBasicSecurityRequirementAsV3JsonWorks() actual.Should().Be(expected); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeSecurityRequirementAsV3JsonWorksAsync(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); + + // Act + SecurityRequirementWithReferencedSecurityScheme.SerializeAsV3(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + [Fact] public void SerializeSecurityRequirementWithReferencedSecuritySchemeAsV3JsonWorks() { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 1de104df5..39613f3ae 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "type": "openIdConnect", "description": "description1", "openIdConnectUrl": "https://example.com/openIdConnect" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 5e7183dc8..717cba0a8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"type":"openIdConnect","description":"description1","openIdConnectUrl":"https://example.com/openIdConnect"} \ No newline at end of file +{"type":"openIdConnect","description":"description1","openIdConnectUrl":"https://example.com/openIdConnect"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index e2f0188e6..e17924528 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,3 +1,3 @@ -{ - "sampleSecurityScheme": null +{ + "$ref": "sampleSecurityScheme" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index d74ff6ddf..3866a27aa 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.SerializeReferencedSecuritySchemeAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"sampleSecurityScheme":null} \ No newline at end of file +{"$ref":"sampleSecurityScheme"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs index 68b5867ea..58794373d 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs @@ -311,12 +311,7 @@ public async Task SerializeReferencedSecuritySchemeAsV3JsonWorksAsync(bool produ var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput }); // Act - // Add dummy start object, value, and end object to allow SerializeAsV3 to output security scheme - // as property name. - writer.WriteStartObject(); OpenApiSecuritySchemeReference.SerializeAsV3(writer); - writer.WriteNull(); - writer.WriteEndObject(); writer.Flush(); // Assert diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 4e4df0f3b..2afa516e0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "name": "pet", "description": "Pets operations", "externalDocs": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 269fd9e7f..f0a901938 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null} \ No newline at end of file +{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 4e4df0f3b..2afa516e0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "name": "pet", "description": "Pets operations", "externalDocs": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 269fd9e7f..f0a901938 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null} \ No newline at end of file +{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeAdvancedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 6f31cf5a2..87b8ff218 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -{ } \ No newline at end of file +{ } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 9e26dfeeb..22fdca1b2 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{} \ No newline at end of file +{} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index 6f31cf5a2..87b8ff218 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -{ } \ No newline at end of file +{ } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt index 9e26dfeeb..22fdca1b2 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{} \ No newline at end of file +{} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt index 8c38cc78d..d3d287dca 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.SerializeReferencedTagAsV3JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -"pet" \ No newline at end of file +"pet" \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt old mode 100755 new mode 100644 index f53297f91..3a7fdbd57 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -184,6 +184,10 @@ namespace Microsoft.OpenApi.Extensions public static void SerializeAsYaml(this T element, System.IO.Stream stream, Microsoft.OpenApi.OpenApiSpecVersion specVersion) where T : Microsoft.OpenApi.Interfaces.IOpenApiSerializable { } } + public static class OpenApiServerExtensions + { + public static string ReplaceServerUrlVariables(this Microsoft.OpenApi.Models.OpenApiServer server, System.Collections.Generic.IDictionary values = null) { } + } public static class OpenApiTypeMapper { public static System.Type MapOpenApiPrimitiveTypeToSimpleType(this Microsoft.OpenApi.Models.OpenApiSchema schema) { } @@ -197,6 +201,10 @@ namespace Microsoft.OpenApi.Extensions namespace Microsoft.OpenApi.Interfaces { public interface IDiagnostic { } + public interface IOpenApiAnnotatable + { + System.Collections.Generic.IDictionary Annotations { get; set; } + } public interface IOpenApiElement { } public interface IOpenApiExtensible : Microsoft.OpenApi.Interfaces.IOpenApiElement { @@ -228,6 +236,7 @@ namespace Microsoft.OpenApi.Interfaces } public interface IStreamLoader { + [System.Obsolete("Use the Async overload")] System.IO.Stream Load(System.Uri uri); System.Threading.Tasks.Task LoadAsync(System.Uri uri); } @@ -530,10 +539,11 @@ namespace Microsoft.OpenApi.Models public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } } - public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable + public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiDocument() { } public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument? document) { } + public System.Collections.Generic.IDictionary? Annotations { get; set; } public System.Uri BaseUri { get; } public Microsoft.OpenApi.Models.OpenApiComponents? Components { get; set; } public System.Collections.Generic.IDictionary? Extensions { get; set; } @@ -726,11 +736,12 @@ namespace Microsoft.OpenApi.Models public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } } - public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable + public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public const bool DeprecatedDefault = false; public OpenApiOperation() { } public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation? operation) { } + public System.Collections.Generic.IDictionary? Annotations { get; set; } public System.Collections.Generic.IDictionary? Callbacks { get; set; } public bool Deprecated { get; set; } public string? Description { get; set; } @@ -847,10 +858,11 @@ namespace Microsoft.OpenApi.Models public OpenApiResponses() { } public OpenApiResponses(Microsoft.OpenApi.Models.OpenApiResponses openApiResponses) { } } - public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable + public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { public OpenApiSchema() { } public OpenApiSchema(Microsoft.OpenApi.Models.OpenApiSchema schema) { } + public System.Collections.Generic.IDictionary Annotations { get; set; } public virtual Microsoft.OpenApi.Models.OpenApiSchema AdditionalProperties { get; set; } public virtual bool AdditionalPropertiesAllowed { get; set; } public virtual System.Collections.Generic.IList AllOf { get; set; } @@ -1315,6 +1327,7 @@ namespace Microsoft.OpenApi.Reader public static Microsoft.OpenApi.Reader.ReadResult Parse(string input, string format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } public static T Parse(string input, Microsoft.OpenApi.OpenApiSpecVersion version, out Microsoft.OpenApi.Reader.OpenApiDiagnostic diagnostic, string format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { } + public static System.Threading.Tasks.Task ParseAsync(string input, System.IO.StringReader reader, string format = null, Microsoft.OpenApi.Reader.OpenApiReaderSettings settings = null) { } } public static class OpenApiReaderRegistry { @@ -1384,6 +1397,7 @@ namespace Microsoft.OpenApi.Reader.Services public class DefaultStreamLoader : Microsoft.OpenApi.Interfaces.IStreamLoader { public DefaultStreamLoader(System.Uri baseUrl) { } + [System.Obsolete] public System.IO.Stream Load(System.Uri uri) { } public System.Threading.Tasks.Task LoadAsync(System.Uri uri) { } } @@ -1515,7 +1529,7 @@ namespace Microsoft.OpenApi.Services public System.Uri GetDocumentId(string key) { } public bool RegisterComponent(string location, T component) { } public void RegisterComponents(Microsoft.OpenApi.Models.OpenApiDocument document) { } - public T ResolveReference(string location) { } + public T? ResolveReference(string location) { } } public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase { diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.VerifyDiagramFromSampleOpenAPI.verified.txt b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.VerifyDiagramFromSampleOpenAPIAsync.verified.txt similarity index 100% rename from test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.VerifyDiagramFromSampleOpenAPI.verified.txt rename to test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.VerifyDiagramFromSampleOpenAPIAsync.verified.txt diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index d92fe0493..09ef9b04d 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -452,7 +452,7 @@ public void ThrowsArgumentNullExceptionForNullArgumentInAddAdditionalDataMethod( } [Fact] - public async Task VerifyDiagramFromSampleOpenAPI() + public async Task VerifyDiagramFromSampleOpenAPIAsync() { var doc1 = OpenApiDocumentSample_1; @@ -461,10 +461,43 @@ public async Task VerifyDiagramFromSampleOpenAPI() var writer = new StringWriter(); rootNode.WriteMermaid(writer); - writer.Flush(); + await writer.FlushAsync(); var diagram = writer.GetStringBuilder().ToString(); await Verifier.Verify(diagram); } + + public static TheoryData SupportsTrailingSlashesInPathData => new TheoryData + { + // Path, children up to second to leaf, last expected leaf node name, expected leaf node path + { "/cars/{car-id}/build/", ["cars", "{car-id}"], @"build\", @"\cars\{car-id}\build\" }, + { "/cars/", [], @"cars\", @"\cars\" }, + }; + + [Theory] + [MemberData(nameof(SupportsTrailingSlashesInPathData))] + public void SupportsTrailingSlashesInPath(string path, string[] childrenBeforeLastNode, string expectedLeafNodeName, string expectedLeafNodePath) + { + var openApiDocument = new OpenApiDocument + { + Paths = new() + { + [path] = new() + } + }; + + var label = "trailing-slash"; + var rootNode = OpenApiUrlTreeNode.Create(openApiDocument, label); + + var secondToLeafNode = rootNode; + foreach (var childName in childrenBeforeLastNode) + { + secondToLeafNode = secondToLeafNode.Children[childName]; + } + + Assert.True(secondToLeafNode.Children.TryGetValue(expectedLeafNodeName, out var leafNode)); + Assert.Equal(expectedLeafNodePath, leafNode.Path); + Assert.Empty(leafNode.Children); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs index 4bc7e7cfd..15ef6b07f 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs @@ -1,10 +1,12 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Models; +using System.Reflection; +using Microsoft.OpenApi.Validations.Rules; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -188,5 +190,25 @@ public void ClearAllRulesWorks() // Assert Assert.Empty(ruleSet.Rules); } + + [Fact] + public void GetValidationRuleTypesReturnsAllSupportedTypes() + { + var declaredRuleTypeProperties = typeof(ValidationRuleSet).Assembly.GetTypes() + .Where(t => t.IsClass + && t != typeof(object) + && t.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any()) + .SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public)) + .ToList(); + + var resolvedRuleTypeProperties = ValidationRuleSet.GetValidationRuleTypes(); + + foreach (var ruleTypeProperty in resolvedRuleTypeProperties) + { + Assert.True(declaredRuleTypeProperties.Remove(ruleTypeProperty)); + } + + Assert.Empty(declaredRuleTypeProperties); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 100% rename from test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorksAsync_produceTerseOutput=False.verified.txt diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorksAsync_produceTerseOutput=True.verified.txt similarity index 100% rename from test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_produceTerseOutput=True.verified.txt rename to test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorksAsync_produceTerseOutput=True.verified.txt diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=False.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=False.verified.txt deleted file mode 100644 index 1a91b1047..000000000 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=False.verified.txt +++ /dev/null @@ -1,11 +0,0 @@ -[ - false, - { - "stringProp": "stringValue1", - "objProp": { }, - "arrayProp": [ - false - ] - }, - "stringValue2" -] \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=True.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=True.verified.txt deleted file mode 100644 index 75f913cf2..000000000 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiArrayAsJsonWorks_terse=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -[false,{"stringProp":"stringValue1","objProp":{},"arrayProp":[false]},"stringValue2"] \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=False.verified.txt similarity index 94% rename from test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=False.verified.txt rename to test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=False.verified.txt index 1b6b4d799..c7812132a 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "stringProp": "stringValue1", "objProp": { }, "arrayProp": [ diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..f9a0cd869 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorksAsync_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"stringProp":"stringValue1","objProp":{},"arrayProp":[false]} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=True.verified.txt deleted file mode 100644 index c2132cb78..000000000 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.WriteOpenApiObjectAsJsonWorks_produceTerseOutput=True.verified.txt +++ /dev/null @@ -1 +0,0 @@ -{"stringProp":"stringValue1","objProp":{},"arrayProp":[false]} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs index 96e8027a0..2d966e8a5 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs @@ -25,10 +25,10 @@ public class OpenApiWriterAnyExtensionsTests [Theory] [InlineData(true)] [InlineData(false)] - public void WriteOpenApiNullAsJsonWorks(bool produceTerseOutput) + public async Task WriteOpenApiNullAsJsonWorksAsync(bool produceTerseOutput) { // Arrange - var json = WriteAsJson(null, produceTerseOutput); + var json = await WriteAsJsonAsync(null, produceTerseOutput); // Assert json.Should().Be("null"); @@ -51,12 +51,12 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(IntInputs))] - public void WriteOpenApiIntegerAsJsonWorks(int input, bool produceTerseOutput) + public async Task WriteOpenApiIntegerAsJsonWorksAsync(int input, bool produceTerseOutput) { // Arrange var intValue = input; - var json = WriteAsJson(intValue, produceTerseOutput); + var json = await WriteAsJsonAsync(intValue, produceTerseOutput); // Assert json.Should().Be(input.ToString()); @@ -79,12 +79,12 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(LongInputs))] - public void WriteOpenApiLongAsJsonWorks(long input, bool produceTerseOutput) + public async Task WriteOpenApiLongAsJsonWorksAsync(long input, bool produceTerseOutput) { // Arrange var longValue = input; - var json = WriteAsJson(longValue, produceTerseOutput); + var json = await WriteAsJsonAsync(longValue, produceTerseOutput); // Assert json.Should().Be(input.ToString()); @@ -107,12 +107,12 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(FloatInputs))] - public void WriteOpenApiFloatAsJsonWorks(float input, bool produceTerseOutput) + public async Task WriteOpenApiFloatAsJsonWorksAsync(float input, bool produceTerseOutput) { // Arrange var floatValue = input; - var json = WriteAsJson(floatValue, produceTerseOutput); + var json = await WriteAsJsonAsync(floatValue, produceTerseOutput); // Assert json.Should().Be(input.ToString()); @@ -135,12 +135,12 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(DoubleInputs))] - public void WriteOpenApiDoubleAsJsonWorks(double input, bool produceTerseOutput) + public async Task WriteOpenApiDoubleAsJsonWorksAsync(double input, bool produceTerseOutput) { // Arrange var doubleValue = input; - var json = WriteAsJson(doubleValue, produceTerseOutput); + var json = await WriteAsJsonAsync(doubleValue, produceTerseOutput); // Assert json.Should().Be(input.ToString()); @@ -164,13 +164,13 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(StringifiedDateTimes))] - public void WriteOpenApiDateTimeAsJsonWorks(string inputString, bool produceTerseOutput) + public async Task WriteOpenApiDateTimeAsJsonWorksAsync(string inputString, bool produceTerseOutput) { // Arrange var input = DateTimeOffset.Parse(inputString, CultureInfo.InvariantCulture); var dateTimeValue = input; - var json = WriteAsJson(dateTimeValue, produceTerseOutput); + var json = await WriteAsJsonAsync(dateTimeValue, produceTerseOutput); var expectedJson = "\"" + input.ToString("o") + "\""; // Assert @@ -187,12 +187,12 @@ from shouldBeTerse in shouldProduceTerseOutputValues [Theory] [MemberData(nameof(BooleanInputs))] - public void WriteOpenApiBooleanAsJsonWorks(bool input, bool produceTerseOutput) + public async Task WriteOpenApiBooleanAsJsonWorksAsync(bool input, bool produceTerseOutput) { // Arrange var boolValue = input; - var json = WriteAsJson(boolValue, produceTerseOutput); + var json = await WriteAsJsonAsync(boolValue, produceTerseOutput); // Assert json.Should().Be(input.ToString().ToLower()); @@ -201,7 +201,7 @@ public void WriteOpenApiBooleanAsJsonWorks(bool input, bool produceTerseOutput) [Theory] [InlineData(true)] [InlineData(false)] - public async Task WriteOpenApiObjectAsJsonWorks(bool produceTerseOutput) + public async Task WriteOpenApiObjectAsJsonWorksAsync(bool produceTerseOutput) { // Arrange var openApiObject = new JsonObject @@ -217,7 +217,7 @@ public async Task WriteOpenApiObjectAsJsonWorks(bool produceTerseOutput) } }; - var actualJson = WriteAsJson(openApiObject, produceTerseOutput); + var actualJson = WriteAsJsonAsync(openApiObject, produceTerseOutput); // Assert await Verifier.Verify(actualJson).UseParameters(produceTerseOutput); @@ -226,7 +226,7 @@ public async Task WriteOpenApiObjectAsJsonWorks(bool produceTerseOutput) [Theory] [InlineData(true)] [InlineData(false)] - public async Task WriteOpenApiArrayAsJsonWorks(bool produceTerseOutput) + public async Task WriteOpenApiArrayAsJsonWorksAsync(bool produceTerseOutput) { // Arrange var openApiObject = new JsonObject @@ -249,17 +249,17 @@ public async Task WriteOpenApiArrayAsJsonWorks(bool produceTerseOutput) "stringValue2" }; - var actualJson = WriteAsJson(array, produceTerseOutput); + var actualJson = WriteAsJsonAsync(array, produceTerseOutput); // Assert await Verifier.Verify(actualJson).UseParameters(produceTerseOutput); } - private static string WriteAsJson(JsonNode any, bool produceTerseOutput = false) + private static async Task WriteAsJsonAsync(JsonNode any, bool produceTerseOutput = false) { // Arrange (continued) - var stream = new MemoryStream(); - IOpenApiWriter writer = new OpenApiJsonWriter( + using var stream = new MemoryStream(); + var writer = new OpenApiJsonWriter( new StreamWriter(stream), new() { Terse = produceTerseOutput }); @@ -268,7 +268,8 @@ private static string WriteAsJson(JsonNode any, bool produceTerseOutput = false) stream.Position = 0; // Act - var value = new StreamReader(stream).ReadToEnd(); + using var sr = new StreamReader(stream); + var value = await sr.ReadToEndAsync(); var element = JsonDocument.Parse(value).RootElement; return element.ValueKind switch { diff --git a/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj new file mode 100644 index 000000000..3e6daf74c --- /dev/null +++ b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj @@ -0,0 +1,19 @@ + + + Exe + net8.0 + enable + enable + true + false + true + false + + + + + + + + + diff --git a/test/Microsoft.OpenApi.Trimming.Tests/Program.cs b/test/Microsoft.OpenApi.Trimming.Tests/Program.cs new file mode 100644 index 000000000..1bc52a60a --- /dev/null +++ b/test/Microsoft.OpenApi.Trimming.Tests/Program.cs @@ -0,0 +1 @@ +Console.WriteLine("Hello, World!");