diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4b1bdc116..6b7b81454 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,13 +30,13 @@ jobs: id: getversion - name: Push to GitHub Packages - Nightly if: ${{ github.ref == 'refs/heads/vnext' }} - uses: docker/build-push-action@v4.1.0 + uses: docker/build-push-action@v4.1.1 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@v4.1.0 + uses: docker/build-push-action@v4.1.1 with: push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }} diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 004aa48cd..d595f961b 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -15,7 +15,7 @@ Microsoft.OpenApi.Hidi hidi ./../../artifacts - 1.2.5 + 1.2.6 OpenAPI.NET CLI tool for slicing OpenAPI documents © Microsoft Corporation. All rights reserved. OpenAPI .NET @@ -43,7 +43,7 @@ - + diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 81c08752d..ba2cf568d 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -10,7 +10,7 @@ Microsoft Microsoft.OpenApi.Readers Microsoft.OpenApi.Readers - 1.6.5 + 1.6.6 OpenAPI.NET Readers for JSON and YAML documents © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj index d15de65eb..70c120ca0 100644 --- a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj +++ b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj @@ -5,12 +5,13 @@ false true true + true all - + diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 084742af2..d727c4627 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -11,7 +11,7 @@ Microsoft Microsoft.OpenApi Microsoft.OpenApi - 1.6.5 + 1.6.6 .NET models with JSON and YAML writers for OpenAPI specification © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index c172d84c7..d2d9cf893 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -290,6 +290,18 @@ public void SerializeAsV2(IOpenApiWriter writer) writer.WriteEndObject(); } + 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; + } + private static void WriteHostInfoV2(IOpenApiWriter writer, IList servers) { if (servers == null || !servers.Any()) @@ -299,11 +311,11 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList // Arbitrarily choose the first server given that V2 only allows // one host, port, and base path. - var firstServer = servers.First(); + var serverUrl = ParseServerUrl(servers.First()); // Divide the URL in the Url property into host and basePath required in OpenAPI V2 // The Url property cannotcontain path templating to be valid for V2 serialization. - var firstServerUrl = new Uri(firstServer.Url, UriKind.RelativeOrAbsolute); + var firstServerUrl = new Uri(serverUrl, UriKind.RelativeOrAbsolute); // host if (firstServerUrl.IsAbsoluteUri) @@ -337,7 +349,7 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList var schemes = servers.Select( s => { - Uri.TryCreate(s.Url, UriKind.RelativeOrAbsolute, out var url); + Uri.TryCreate(ParseServerUrl(s), UriKind.RelativeOrAbsolute, out var url); return url; }) .Where( 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 87fade6cd..baeed8767 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -13,10 +13,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index 3733ad848..3c039b9aa 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs @@ -51,7 +51,7 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIdsAndTags(string opera public void ReturnFilteredOpenApiDocumentBasedOnPostmanCollection() { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\postmanCollection_ver2.json"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "postmanCollection_ver2.json"); var fileInput = new FileInfo(filePath); var stream = fileInput.OpenRead(); @@ -107,7 +107,7 @@ public void TestPredicateFiltersUsingRelativeRequestUrls() public void ShouldParseNestedPostmanCollection() { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\postmanCollection_ver3.json"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "postmanCollection_ver3.json"); var fileInput = new FileInfo(filePath); var stream = fileInput.OpenRead(); @@ -124,7 +124,7 @@ public void ShouldParseNestedPostmanCollection() public void ThrowsExceptionWhenUrlsInCollectionAreMissingFromSourceDocument() { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\postmanCollection_ver1.json"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "postmanCollection_ver1.json"); var fileInput = new FileInfo(filePath); var stream = fileInput.OpenRead(); @@ -141,7 +141,7 @@ public void ThrowsExceptionWhenUrlsInCollectionAreMissingFromSourceDocument() public void ContinueProcessingWhenUrlsInCollectionAreMissingFromSourceDocument() { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\postmanCollection_ver4.json"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "postmanCollection_ver4.json"); var fileInput = new FileInfo(filePath); var stream = fileInput.OpenRead(); diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index c092da510..49f1bbd96 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -30,7 +30,7 @@ public OpenApiServiceTests() public async Task ReturnConvertedCSDLFile() { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\Todo.xml"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "Todo.xml"); var fileInput = new FileInfo(filePath); var csdlStream = fileInput.OpenRead(); // Act @@ -50,7 +50,7 @@ public async Task ReturnConvertedCSDLFile() public async Task ReturnFilteredOpenApiDocBasedOnOperationIdsAndInputCsdlDocument(string operationIds, string tags, int expectedPathCount) { // Arrange - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\Todo.xml"); + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "Todo.xml"); var fileInput = new FileInfo(filePath); var csdlStream = fileInput.OpenRead(); @@ -137,7 +137,7 @@ public async Task ShowCommandGeneratesMermaidMarkdownFileWithMermaidDiagram() // create a dummy ILogger instance for testing var options = new HidiOptions() { - OpenApi = "UtilityFiles\\SampleOpenApi.yml", + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml"), Output = new FileInfo("sample.md") }; @@ -152,7 +152,7 @@ public async Task ShowCommandGeneratesMermaidHtmlFileWithMermaidDiagram() { var options = new HidiOptions() { - OpenApi = "UtilityFiles\\SampleOpenApi.yml" + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml") }; var filePath = await OpenApiService.ShowOpenApiDocument(options, _logger, new CancellationToken()); Assert.True(File.Exists(filePath)); @@ -163,7 +163,7 @@ public async Task ShowCommandGeneratesMermaidMarkdownFileFromCsdlWithMermaidDiag { var options = new HidiOptions() { - Csdl = "UtilityFiles\\Todo.xml", + Csdl = Path.Combine("UtilityFiles", "Todo.xml"), CsdlFilter = "todos", Output = new FileInfo("sample.md") }; @@ -201,7 +201,7 @@ await Assert.ThrowsAsync(async () => public async Task ValidateCommandProcessesOpenApi() { // create a dummy ILogger instance for testing - await OpenApiService.ValidateOpenApiDocument("UtilityFiles\\SampleOpenApi.yml", _logger, new CancellationToken()); + await OpenApiService.ValidateOpenApiDocument(Path.Combine("UtilityFiles", "SampleOpenApi.yml"), _logger, new CancellationToken()); Assert.True(true); } @@ -212,7 +212,7 @@ public async Task TransformCommandConvertsOpenApi() { HidiOptions options = new HidiOptions { - OpenApi = "UtilityFiles\\SampleOpenApi.yml", + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml"), Output = new FileInfo("sample.json"), CleanOutput = true, TerseOutput = false, @@ -232,7 +232,7 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputname() { HidiOptions options = new HidiOptions { - OpenApi = "UtilityFiles\\SampleOpenApi.yml", + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml"), CleanOutput = true, TerseOutput = false, InlineLocal = false, @@ -250,7 +250,7 @@ public async Task TransformCommandConvertsCsdlWithDefaultOutputname() { HidiOptions options = new HidiOptions { - Csdl = "UtilityFiles\\Todo.xml", + Csdl = Path.Combine("UtilityFiles", "Todo.xml"), CleanOutput = true, TerseOutput = false, InlineLocal = false, @@ -268,7 +268,7 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputnameAndSwitchF { HidiOptions options = new HidiOptions { - OpenApi = "UtilityFiles\\SampleOpenApi.yml", + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml"), CleanOutput = true, Version = "3.0", OpenApiFormat = OpenApiFormat.Yaml, @@ -301,10 +301,10 @@ await Assert.ThrowsAsync(async () => [Fact] public async Task TransformToPowerShellCompliantOpenApi() { - var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles\\examplepowershellsettings.json"); + var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UtilityFiles", "examplepowershellsettings.json"); HidiOptions options = new HidiOptions { - OpenApi = "UtilityFiles\\SampleOpenApi.yml", + OpenApi = Path.Combine("UtilityFiles", "SampleOpenApi.yml"), CleanOutput = true, Version = "3.0", OpenApiFormat = OpenApiFormat.Yaml, @@ -324,7 +324,8 @@ public async Task TransformToPowerShellCompliantOpenApi() public void InvokeTransformCommand() { var rootCommand = Program.CreateRootCommand(); - var args = new string[] { "transform", "-d", ".\\UtilityFiles\\SampleOpenApi.yml", "-o", "sample.json", "--co" }; + var openapi = Path.Combine(".", "UtilityFiles", "SampleOpenApi.yml"); + var args = new string[] { "transform", "-d", openapi, "-o", "sample.json", "--co" }; var parseResult = rootCommand.Parse(args); var handler = rootCommand.Subcommands.Where(c => c.Name == "transform").First().Handler; var context = new InvocationContext(parseResult); @@ -340,7 +341,8 @@ public void InvokeTransformCommand() public void InvokeShowCommand() { var rootCommand = Program.CreateRootCommand(); - var args = new string[] { "show", "-d", ".\\UtilityFiles\\SampleOpenApi.yml", "-o", "sample.md" }; + var openapi = Path.Combine(".", "UtilityFiles", "SampleOpenApi.yml"); + var args = new string[] { "show", "-d", openapi, "-o", "sample.md" }; var parseResult = rootCommand.Parse(args); var handler = rootCommand.Subcommands.Where(c => c.Name == "show").First().Handler; var context = new InvocationContext(parseResult); @@ -355,7 +357,8 @@ public void InvokeShowCommand() public void InvokePluginCommand() { var rootCommand = Program.CreateRootCommand(); - var args = new string[] { "plugin", "-m", ".\\UtilityFiles\\exampleapimanifest.json", "--of", AppDomain.CurrentDomain.BaseDirectory }; + var manifest = Path.Combine(".", "UtilityFiles", "exampleapimanifest.json"); + var args = new string[] { "plugin", "-m", manifest, "--of", AppDomain.CurrentDomain.BaseDirectory }; var parseResult = rootCommand.Parse(args); var handler = rootCommand.Subcommands.Where(c => c.Name == "plugin").First().Handler; var context = new InvocationContext(parseResult); 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 97ae2f019..458e8e627 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -268,16 +268,16 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj index 83cf2fc86..10ce38c55 100644 --- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj +++ b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj @@ -16,10 +16,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index b46619c0d..02614f5f2 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -24,13 +24,13 @@ all - + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..1656b2bf7 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=False.verified.txt @@ -0,0 +1,417 @@ +{ + "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 diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..3670fba11 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks_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":"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 diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index 6e3200957..924699bdf 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -981,6 +981,305 @@ public class OpenApiDocumentTests } }; + public OpenApiDocument AdvancedDocumentWithServerVariable = new OpenApiDocument + { + Info = new OpenApiInfo + { + 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 Uri("http://helloreverb.com/terms/"), + Contact = new OpenApiContact + { + Name = "Swagger API team", + Email = "foo@example.com", + Url = new Uri("http://swagger.io") + }, + License = new OpenApiLicense + { + Name = "MIT", + Url = new Uri("http://opensource.org/licenses/MIT") + } + }, + Servers = new List + { + new OpenApiServer + { + Url = "https://{endpoint}/openai", + Variables = new Dictionary + { + ["endpoint"] = new OpenApiServerVariable + { + Default = "your-resource-name.openai.azure.com" + } + } + } + }, + Paths = new OpenApiPaths + { + ["/pets"] = new OpenApiPathItem + { + Operations = new Dictionary + { + [OperationType.Get] = new OpenApiOperation + { + Description = "Returns all pets from the system that the user has access to", + OperationId = "findPets", + Parameters = new List + { + new OpenApiParameter + { + Name = "tags", + In = ParameterLocation.Query, + Description = "tags to filter by", + Required = false, + Schema = new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Type = "string" + } + } + }, + new OpenApiParameter + { + Name = "limit", + In = ParameterLocation.Query, + Description = "maximum number of results to return", + Required = false, + Schema = new OpenApiSchema + { + Type = "integer", + Format = "int32" + } + } + }, + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array", + Items = PetSchema + } + }, + ["application/xml"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array", + Items = PetSchema + } + } + } + }, + ["4XX"] = new OpenApiResponse + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new OpenApiResponse + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + } + } + }, + [OperationType.Post] = new OpenApiOperation + { + Description = "Creates a new pet in the store. Duplicates are allowed", + OperationId = "addPet", + RequestBody = new OpenApiRequestBody + { + Description = "Pet to add to the store", + Required = true, + Content = new Dictionary + { + ["application/json"] = new OpenApiMediaType + { + Schema = NewPetSchema + } + } + }, + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new OpenApiMediaType + { + Schema = PetSchema + }, + } + }, + ["4XX"] = new OpenApiResponse + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new OpenApiResponse + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + } + } + } + } + }, + ["/pets/{id}"] = new OpenApiPathItem + { + Operations = new Dictionary + { + [OperationType.Get] = new OpenApiOperation + { + 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 OpenApiParameter + { + Name = "id", + In = ParameterLocation.Path, + Description = "ID of pet to fetch", + Required = true, + Schema = new OpenApiSchema + { + Type = "integer", + Format = "int64" + } + } + }, + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "pet response", + Content = new Dictionary + { + ["application/json"] = new OpenApiMediaType + { + Schema = PetSchema + }, + ["application/xml"] = new OpenApiMediaType + { + Schema = PetSchema + } + } + }, + ["4XX"] = new OpenApiResponse + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new OpenApiResponse + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + } + } + }, + [OperationType.Delete] = new OpenApiOperation + { + Description = "deletes a single pet based on the ID supplied", + OperationId = "deletePet", + Parameters = new List + { + new OpenApiParameter + { + Name = "id", + In = ParameterLocation.Path, + Description = "ID of pet to delete", + Required = true, + Schema = new OpenApiSchema + { + Type = "integer", + Format = "int64" + } + } + }, + Responses = new OpenApiResponses + { + ["204"] = new OpenApiResponse + { + Description = "pet deleted" + }, + ["4XX"] = new OpenApiResponse + { + Description = "unexpected client error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + }, + ["5XX"] = new OpenApiResponse + { + Description = "unexpected server error", + Content = new Dictionary + { + ["text/html"] = new OpenApiMediaType + { + Schema = ErrorModelSchema + } + } + } + } + } + } + } + }, + Components = AdvancedComponents + }; + private readonly ITestOutputHelper _output; public OpenApiDocumentTests(ITestOutputHelper output) @@ -1022,6 +1321,23 @@ public async Task SerializeAdvancedDocumentWithReferenceAsV3JsonWorks(bool produ await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeAdvancedDocumentWithServerVariableAsV2JsonWorks(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput }); + + // Act + AdvancedDocumentWithServerVariable.SerializeAsV2(writer); + writer.Flush(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + [Theory] [InlineData(true)] [InlineData(false)]