diff --git a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs index d0a49c209..e00cd7efa 100644 --- a/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs +++ b/src/Microsoft.OpenApi.Hidi/Handlers/TransformCommandHandler.cs @@ -19,6 +19,7 @@ internal class TransformCommandHandler : ICommandHandler public Option OutputOption { get; set; } public Option CleanOutputOption { get; set; } public Option VersionOption { get; set; } + public Option MetadataVersionOption { get; set; } public Option FormatOption { get; set; } public Option TerseOutputOption { get; set; } public Option SettingsFileOption { get; set; } @@ -41,6 +42,7 @@ public async Task InvokeAsync(InvocationContext context) FileInfo output = context.ParseResult.GetValueForOption(OutputOption); bool cleanOutput = context.ParseResult.GetValueForOption(CleanOutputOption); string? version = context.ParseResult.GetValueForOption(VersionOption); + string metadataVersion = context.ParseResult.GetValueForOption(MetadataVersionOption); OpenApiFormat? format = context.ParseResult.GetValueForOption(FormatOption); bool terseOutput = context.ParseResult.GetValueForOption(TerseOutputOption); string settingsFile = context.ParseResult.GetValueForOption(SettingsFileOption); @@ -57,7 +59,7 @@ public async Task InvokeAsync(InvocationContext context) var logger = loggerFactory.CreateLogger(); try { - await OpenApiService.TransformOpenApiDocument(openapi, csdl, csdlFilter, output, cleanOutput, version, format, terseOutput, settingsFile, inlineLocal, inlineExternal, filterbyoperationids, filterbytags, filterbycollection, logger, cancellationToken); + await OpenApiService.TransformOpenApiDocument(openapi, csdl, csdlFilter, output, cleanOutput, version, metadataVersion, format, terseOutput, settingsFile, inlineLocal, inlineExternal, filterbyoperationids, filterbytags, filterbycollection, logger, cancellationToken); return 0; } diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 98f250626..a018775cb 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.4 + 1.2.5-preview1 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.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index 4d535ee02..5d5ec95d4 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; using System.Security; @@ -20,7 +19,6 @@ using Microsoft.OpenApi.OData; using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Services; -using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Writers; using static Microsoft.OpenApi.Hidi.OpenApiSpecVersionHelper; using System.Threading; @@ -43,6 +41,7 @@ public static async Task TransformOpenApiDocument( FileInfo output, bool cleanoutput, string? version, + string metadataVersion, OpenApiFormat? format, bool terseOutput, string settingsFile, @@ -81,7 +80,7 @@ CancellationToken cancellationToken OpenApiFormat openApiFormat = format ?? (!string.IsNullOrEmpty(openapi) ? GetOpenApiFormat(openapi, logger) : OpenApiFormat.Yaml); OpenApiSpecVersion openApiVersion = version != null ? TryParseOpenApiSpecVersion(version) : OpenApiSpecVersion.OpenApi3_0; - OpenApiDocument document = await GetOpenApi(openapi, csdl, csdlFilter, settingsFile, inlineExternal, logger, cancellationToken); + OpenApiDocument document = await GetOpenApi(openapi, csdl, csdlFilter, settingsFile, inlineExternal, logger, cancellationToken, metadataVersion); document = await FilterOpenApiDocument(filterbyoperationids, filterbytags, filterbycollection, document, logger, cancellationToken); WriteOpenApi(output, terseOutput, inlineLocal, inlineExternal, openApiFormat, openApiVersion, document, logger); } @@ -132,11 +131,11 @@ private static void WriteOpenApi(FileInfo output, bool terseOutput, bool inlineL } // Get OpenAPI document either from OpenAPI or CSDL - private static async Task GetOpenApi(string openapi, string csdl, string csdlFilter, string settingsFile, bool inlineExternal, ILogger logger, CancellationToken cancellationToken) + private static async Task GetOpenApi(string openapi, string csdl, string csdlFilter, string settingsFile, bool inlineExternal, ILogger logger, CancellationToken cancellationToken, string metadataVersion = null) { OpenApiDocument document; Stream stream; - + if (!string.IsNullOrEmpty(csdl)) { var stopwatch = new Stopwatch(); @@ -154,7 +153,7 @@ private static async Task GetOpenApi(string openapi, string csd stream = null; } - document = await ConvertCsdlToOpenApi(filteredStream ?? stream, settingsFile, cancellationToken); + document = await ConvertCsdlToOpenApi(filteredStream ?? stream, metadataVersion, settingsFile, cancellationToken); stopwatch.Stop(); logger.LogTrace("{timestamp}ms: Generated OpenAPI with {paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count); } @@ -317,7 +316,7 @@ internal static IConfiguration GetConfiguration(string settingsFile) /// /// The CSDL stream. /// An OpenAPI document. - public static async Task ConvertCsdlToOpenApi(Stream csdl, string settingsFile = null, CancellationToken token = default) + public static async Task ConvertCsdlToOpenApi(Stream csdl, string metadataVersion = null, string settingsFile = null, CancellationToken token = default) { using var reader = new StreamReader(csdl); var csdlText = await reader.ReadToEndAsync(token); @@ -325,6 +324,12 @@ public static async Task ConvertCsdlToOpenApi(Stream csdl, stri var config = GetConfiguration(settingsFile); var settings = new OpenApiConvertSettings(); + + if (!string.IsNullOrEmpty(metadataVersion)) + { + settings.SemVerVersion = metadataVersion; + } + config.GetSection("OpenApiConvertSettings").Bind(settings); OpenApiDocument document = edmModel.ConvertToOpenApi(settings); diff --git a/src/Microsoft.OpenApi.Hidi/Program.cs b/src/Microsoft.OpenApi.Hidi/Program.cs index 8d3cc3243..5c5bf691a 100644 --- a/src/Microsoft.OpenApi.Hidi/Program.cs +++ b/src/Microsoft.OpenApi.Hidi/Program.cs @@ -46,9 +46,12 @@ internal static RootCommand CreateRootCommand() var versionOption = new Option("--version", "OpenAPI specification version"); versionOption.AddAlias("-v"); + var metadataVersionOption = new Option("--metadata-version", "Graph metadata version to use."); + metadataVersionOption.AddAlias("--mv"); + var formatOption = new Option("--format", "File format"); formatOption.AddAlias("-f"); - + var terseOutputOption = new Option("--terse-output", "Produce terse json output"); terseOutputOption.AddAlias("--to"); @@ -93,6 +96,7 @@ internal static RootCommand CreateRootCommand() outputOption, cleanOutputOption, versionOption, + metadataVersionOption, formatOption, terseOutputOption, settingsFileOption, @@ -112,6 +116,7 @@ internal static RootCommand CreateRootCommand() OutputOption = outputOption, CleanOutputOption = cleanOutputOption, VersionOption = versionOption, + MetadataVersionOption = metadataVersionOption, FormatOption = formatOption, TerseOutputOption = terseOutputOption, SettingsFileOption = settingsFileOption, diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index f037a02e7..e73151943 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.3 + 1.6.4-preview1 OpenAPI.NET Readers for JSON and YAML documents © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 43ef3876f..86f49db98 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.3 + 1.6.4-preview1 .NET models with JSON and YAML writers for OpenAPI specification © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index d047b9cb6..a1d822a8e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -116,13 +116,13 @@ public OpenApiOperation() {} /// public OpenApiOperation(OpenApiOperation operation) { - Tags = new List(operation?.Tags); + Tags = operation?.Tags != null ? new List(operation?.Tags) : null; Summary = operation?.Summary ?? Summary; Description = operation?.Description ?? Description; ExternalDocs = operation?.ExternalDocs != null ? new(operation?.ExternalDocs) : null; OperationId = operation?.OperationId ?? OperationId; Parameters = operation?.Parameters != null ? new List(operation.Parameters) : null; - RequestBody = new(operation?.RequestBody); + RequestBody = operation?.RequestBody != null ? new(operation?.RequestBody) : null; Responses = operation?.Responses != null ? new(operation?.Responses) : null; Callbacks = operation?.Callbacks != null ? new Dictionary(operation.Callbacks) : null; Deprecated = operation?.Deprecated ?? Deprecated; diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index fa62f0b79..616422165 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -243,11 +243,11 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer) // style if (_style.HasValue) { - writer.WriteProperty(OpenApiConstants.Style, Style.Value.GetDisplayName()); + writer.WriteProperty(OpenApiConstants.Style, _style.Value.GetDisplayName()); } // explode - writer.WriteProperty(OpenApiConstants.Explode, Explode, Style.HasValue && Style.Value == ParameterStyle.Form); + writer.WriteProperty(OpenApiConstants.Explode, _explode, _style.HasValue && _style.Value == ParameterStyle.Form); // allowReserved writer.WriteProperty(OpenApiConstants.AllowReserved, AllowReserved, false); diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index f95acd879..9081c49f5 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -38,9 +38,9 @@ public async Task ReturnConvertedCSDLFile() } [Theory] - [InlineData("Todos.Todo.UpdateTodo",null, 1)] + [InlineData("Todos.Todo.UpdateTodo", null, 1)] [InlineData("Todos.Todo.ListTodo", null, 1)] - [InlineData(null, "Todos.Todo", 4)] + [InlineData(null, "Todos.Todo", 5)] public async Task ReturnFilteredOpenApiDocBasedOnOperationIdsAndInputCsdlDocument(string operationIds, string tags, int expectedPathCount) { // Arrange @@ -190,7 +190,7 @@ public async Task TransformCommandConvertsOpenApi() { var fileinfo = new FileInfo("sample.json"); // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml",null, null, fileinfo, true, null, null,false,null,false,false,null,null,null,new Logger(new LoggerFactory()), new CancellationToken()); + await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml",null, null, fileinfo, true, null, null, null,false,null,false,false,null,null,null,new Logger(new LoggerFactory()), new CancellationToken()); var output = File.ReadAllText("sample.json"); Assert.NotEmpty(output); @@ -201,7 +201,7 @@ public async Task TransformCommandConvertsOpenApi() public async Task TransformCommandConvertsOpenApiWithDefaultOutputname() { // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml", null, null, null, true, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); + await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml", null, null, null, true, null, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); var output = File.ReadAllText("output.yml"); Assert.NotEmpty(output); @@ -211,7 +211,7 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputname() public async Task TransformCommandConvertsCsdlWithDefaultOutputname() { // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument(null, "UtilityFiles\\Todo.xml", null, null, true, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); + await OpenApiService.TransformOpenApiDocument(null, "UtilityFiles\\Todo.xml", null, null, true, null, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); var output = File.ReadAllText("output.yml"); Assert.NotEmpty(output); @@ -221,7 +221,7 @@ public async Task TransformCommandConvertsCsdlWithDefaultOutputname() public async Task TransformCommandConvertsOpenApiWithDefaultOutputnameAndSwitchFormat() { // create a dummy ILogger instance for testing - await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml", null, null, null, true, "3.0", OpenApiFormat.Yaml, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); + await OpenApiService.TransformOpenApiDocument("UtilityFiles\\SampleOpenApi.yml", null, null, null, true, "3.0", null, OpenApiFormat.Yaml, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken()); var output = File.ReadAllText("output.yml"); Assert.NotEmpty(output); @@ -231,7 +231,7 @@ public async Task TransformCommandConvertsOpenApiWithDefaultOutputnameAndSwitchF public async Task ThrowTransformCommandIfOpenApiAndCsdlAreEmpty() { await Assert.ThrowsAsync(async () => - await OpenApiService.TransformOpenApiDocument(null, null, null, null, true, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken())); + await OpenApiService.TransformOpenApiDocument(null, null, null, null, true, null, null, null, false, null, false, false, null, null, null, new Logger(new LoggerFactory()), new CancellationToken())); } 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 aef3d92e0..ded741223 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -267,7 +267,7 @@ - + diff --git a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj index dd8e36a25..b2820b16a 100644 --- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj +++ b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj @@ -17,7 +17,7 @@ all - + all diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index b153ba204..f79d30627 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -26,9 +26,9 @@ - + - + all diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=True.verified.txt index 2ac8e39bb..be8dcc627 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +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","style":"form","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/pet"}}},"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/pet"}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}},"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":{"$ref":"#/components/schemas/newPet"}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/pet"}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}}},"/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,"style":"simple","schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/pet"}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}},"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,"style":"simple","schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}}}},"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 +{"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":{"$ref":"#/components/schemas/pet"}}},"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/pet"}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}},"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":{"$ref":"#/components/schemas/newPet"}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/pet"}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}}},"/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":{"$ref":"#/components/schemas/pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/pet"}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}},"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":{"$ref":"#/components/schemas/errorModel"}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"$ref":"#/components/schemas/errorModel"}}}}}}}},"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 index c1eca99f6..da61a8817 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +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,"style":"simple","schema":{"type":"integer","my-extension":4},"my-extension":4},{"name":"operand2","in":"path","description":"The second operand","required":true,"style":"simple","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 +{"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/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs index 2079a3122..de56df52e 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs @@ -6,6 +6,7 @@ using FluentAssertions; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; +using NuGet.Frameworks; using Xunit; using Xunit.Abstractions; @@ -793,5 +794,58 @@ public void EnsureOpenApiOperationCopyConstructorCopiesResponsesObject() Assert.NotNull(operation.Responses); Assert.Equal(2, operation.Responses.Count); } + + [Fact] + public void EnsureOpenApiOperationCopyConstructorCopiesNull() + { + // Arrange + _basicOperation.Parameters = null; + _basicOperation.Tags = null; + _basicOperation.Responses = null; + _basicOperation.Callbacks = null; + _basicOperation.Security = null; + _basicOperation.Servers = null; + _basicOperation.Extensions = null; + + // Act + var operation = new OpenApiOperation(_basicOperation); + + // Assert + Assert.Null(operation.Tags); + Assert.Null(operation.Summary); + Assert.Null(operation.Description); + Assert.Null(operation.ExternalDocs); + Assert.Null(operation.OperationId); + Assert.Null(operation.Parameters); + Assert.Null(operation.RequestBody); + Assert.Null(operation.Responses); + Assert.Null(operation.Callbacks); + Assert.Null(operation.Security); + Assert.Null(operation.Servers); + Assert.Null(operation.Extensions); + } + + [Fact] + public void EnsureOpenApiOperationCopyConstructor_SerializationResultsInSame() + { + var operations = new[] + { + _basicOperation, + _operationWithBody, + _operationWithFormData, + _advancedOperationWithTagsAndSecurity + }; + + foreach (var operation in operations) + { + // Act + var expected = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + var openApiOperation = new OpenApiOperation(operation); + var actual = openApiOperation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + actual.Should().Be(expected); + } + } } } 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 703bc6613..ec654beb0 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","style":"simple"} \ No newline at end of file +{"name":"name1","in":"path"} \ No newline at end of file