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)]