diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs index d83aa5028c18..b7bff6ff1282 100644 --- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs +++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs @@ -452,11 +452,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -476,7 +485,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -533,12 +542,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -547,7 +565,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs index d63d608c86a4..d349f172b24e 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs @@ -215,7 +215,7 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, additionalAssemblies, docume todo = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The identifier of the todo.", todo.Properties["id"].Description); Assert.Equal("The name of the todo.", todo.Properties["name"].Description); - Assert.Equal("Another description of the todo.", todo.Properties["description"].Description); + Assert.Equal($"A description of the todo.\nAnother description of the todo.", todo.Properties["description"].Description); path = document.Paths["/type-with-examples"].Operations[HttpMethod.Post]; var typeWithExamples = path.RequestBody.Content["application/json"].Schema; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs index 82ae70006c10..24656b12c63f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs @@ -37,6 +37,7 @@ public async Task SupportsAllXmlTagsOnSchemas() app.MapPost("/generic-class", (GenericClass generic) => { }); app.MapPost("/generic-parent", (GenericParent parent) => { }); app.MapPost("/params-and-param-refs", (ParamsAndParamRefs refs) => { }); +app.MapPost("/xml-property-test", (XmlPropertyTestClass test) => { }); app.Run(); @@ -471,6 +472,37 @@ protected virtual void Dispose(bool disposing) // No-op } } + +/// +/// This class tests different XML comment scenarios for properties. +/// +public class XmlPropertyTestClass +{ + /// + /// A property with only summary tag. + /// + public string SummaryOnly { get; set; } + + /// + /// A property with only value tag. + /// + public string ValueOnly { get; set; } + + /// + /// A property with both summary and value. + /// + /// Additional value information. + public string BothSummaryAndValue { get; set; } + + /// This should be ignored for properties. + public string ReturnsOnly { get; set; } + + /// + /// A property with summary and returns. + /// + /// This should be ignored for properties. + public string SummaryAndReturns { get; set; } +} """; var generator = new XmlCommentGenerator(); await SnapshotTestHelper.Verify(source, generator, out var compilation); @@ -479,6 +511,7 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => var path = document.Paths["/example-class"].Operations[HttpMethod.Post]; var exampleClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("Every class and member should have a one sentence\nsummary describing its purpose.", exampleClass.Description, ignoreLineEndingDifferences: true); + // Label property has only tag -> uses value directly Assert.Equal("The `Label` property represents a label\nfor this instance.", exampleClass.Properties["label"].Description, ignoreLineEndingDifferences: true); path = document.Paths["/person"].Operations[HttpMethod.Post]; @@ -523,6 +556,26 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => path = document.Paths["/params-and-param-refs"].Operations[HttpMethod.Post]; var paramsAndParamRefs = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This shows examples of typeparamref and typeparam tags", paramsAndParamRefs.Description); + + // Test new XML property documentation behavior + path = document.Paths["/xml-property-test"].Operations[HttpMethod.Post]; + var xmlPropertyTest = path.RequestBody.Content["application/json"].Schema; + Assert.Equal("This class tests different XML comment scenarios for properties.", xmlPropertyTest.Description); + + // Property with only -> uses summary directly + Assert.Equal("A property with only summary tag.", xmlPropertyTest.Properties["summaryOnly"].Description); + + // Property with only -> uses value directly + Assert.Equal("A property with only value tag.", xmlPropertyTest.Properties["valueOnly"].Description); + + // Property with both and -> combines with newline separator + Assert.Equal($"A property with both summary and value.\nAdditional value information.", xmlPropertyTest.Properties["bothSummaryAndValue"].Description); + + // Property with only -> should be null (ignored) + Assert.Null(xmlPropertyTest.Properties["returnsOnly"].Description); + + // Property with and -> uses summary only, ignores returns + Assert.Equal("A property with summary and returns.", xmlPropertyTest.Properties["summaryAndReturns"].Description); }); } } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs index 33544cd1a1af..dad4380e2f9a 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs @@ -49,6 +49,7 @@ public async Task SupportsXmlCommentsOnOperationsFromMinimalApis() app.MapPost("/19", RouteHandlerExtensionMethods.Post19); app.MapGet("/20", RouteHandlerExtensionMethods.Get20); app.MapGet("/21", RouteHandlerExtensionMethods.Get21); +app.MapGet("/22", RouteHandlerExtensionMethods.Get22); app.Run(); @@ -249,6 +250,15 @@ public static IResult Get21([AsParameters] XmlDocPriorityParametersClass priorit { return TypedResults.Ok($"Processed parameters"); } + + /// + /// Tests summary and value documentation priority on AsParameters properties. + /// + /// Parameters testing summary vs value priority. + public static IResult Get22([AsParameters] SummaryValueParametersClass summaryValueParams) + { + return TypedResults.Ok($"Summary: {summaryValueParams.SummaryProperty}, Value: {summaryValueParams.ValueProperty}"); + } } public class FirstParameters @@ -371,6 +381,23 @@ public class XmlDocPriorityParametersClass /// Value-only description. public string? ValueOnlyProperty { get; set; } } + +public class SummaryValueParametersClass +{ + /// + /// Property with only summary documentation. + /// + public string? SummaryProperty { get; set; } + + /// + /// Property with summary that should be overridden by value. + /// + /// Value description that should take precedence over summary. + public string? ValueProperty { get; set; } + + /// Property with only value documentation. + public string? ValueOnlyProperty { get; set; } +} """; var generator = new XmlCommentGenerator(); await SnapshotTestHelper.Verify(source, generator, out var compilation); @@ -478,16 +505,29 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => Assert.Equal("Property with only summary documentation.", summaryOnlyParam.Description); var summaryAndReturnsParam = path22.Parameters.First(p => p.Name == "SummaryAndReturnsProperty"); - Assert.Equal("Returns-based description that should take precedence over summary.", summaryAndReturnsParam.Description); + Assert.Equal("Property with summary documentation that should be overridden.", summaryAndReturnsParam.Description); var allThreeParam = path22.Parameters.First(p => p.Name == "AllThreeProperty"); - Assert.Equal("Value-based description that should take highest precedence.", allThreeParam.Description); + Assert.Equal($"Property with all three types of documentation.\nValue-based description that should take highest precedence.", allThreeParam.Description); var returnsOnlyParam = path22.Parameters.First(p => p.Name == "ReturnsOnlyProperty"); - Assert.Equal("Returns-only description.", returnsOnlyParam.Description); + Assert.Null(returnsOnlyParam.Description); var valueOnlyParam = path22.Parameters.First(p => p.Name == "ValueOnlyProperty"); Assert.Equal("Value-only description.", valueOnlyParam.Description); + + // Test summary and value documentation priority for AsParameters + var path23 = document.Paths["/22"].Operations[HttpMethod.Get]; + Assert.Equal("Tests summary and value documentation priority on AsParameters properties.", path23.Summary); + + var summaryParam = path23.Parameters.First(p => p.Name == "SummaryProperty"); + Assert.Equal("Property with only summary documentation.", summaryParam.Description); + + var valueParam = path23.Parameters.First(p => p.Name == "ValueProperty"); + Assert.Equal($"Property with summary that should be overridden by value.\nValue description that should take precedence over summary.", valueParam.Description); + + var valueOnlyParam2 = path23.Parameters.First(p => p.Name == "ValueOnlyProperty"); + Assert.Equal("Property with only value documentation.", valueOnlyParam2.Description); }); } } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs index 629b1322c376..9fcd24ca2dec 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs @@ -206,9 +206,13 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => path = document.Paths["/todo-with-description"].Operations[HttpMethod.Post]; todo = path.RequestBody.Content["application/json"].Schema; + // Test different XML comment scenarios for properties: + // Id: only tag -> uses summary directly Assert.Equal("The identifier of the todo.", todo.Properties["id"].Description); + // Name: only tag -> uses value directly Assert.Equal("The name of the todo.", todo.Properties["name"].Description); - Assert.Equal("Another description of the todo.", todo.Properties["description"].Description); + // Description: both and tags -> combines with newline separator + Assert.Equal($"A description of the the todo.\nAnother description of the todo.", todo.Properties["description"].Description); path = document.Paths["/type-with-examples"].Operations[HttpMethod.Post]; var typeWithExamples = path.RequestBody.Content["application/json"].Schema; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs index 80dc82a41ecc..ce2763fd112a 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs @@ -434,11 +434,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -458,7 +467,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -515,12 +524,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -529,7 +547,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs index f5ced40af0d9..42aff6ae7205 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs @@ -463,11 +463,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -487,7 +496,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -544,12 +553,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -558,7 +576,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs index f135f2fc5697..03379781d78f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs @@ -148,6 +148,7 @@ type as a cref attribute. typeof expressions.", null, null, null, null, false, null, null, null)); cache.Add(@"T:ParamsAndParamRefs", new XmlComment(@"This shows examples of typeparamref and typeparam tags", null, null, null, null, false, null, null, null)); cache.Add(@"T:DisposableType", new XmlComment(@"A class that implements the IDisposable interface.", null, null, null, null, false, null, null, null)); + cache.Add(@"T:XmlPropertyTestClass", new XmlComment(@"This class tests different XML comment scenarios for properties.", null, null, null, null, false, null, null, null)); cache.Add(@"P:ExampleClass.Label", new XmlComment(null, null, @" The string? ExampleClass.Label is a `string` that you use for a label. Note that there isn't a way to provide a ""cref"" to @@ -160,6 +161,11 @@ Note that there isn't a way to provide a ""cref"" to cache.Add(@"P:GenericParent.TaskOfTupleProp", new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null)); cache.Add(@"P:GenericParent.TupleWithGenericProp", new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null)); cache.Add(@"P:GenericParent.TupleWithNestedGenericProp", new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null)); + cache.Add(@"P:XmlPropertyTestClass.SummaryOnly", new XmlComment(@"A property with only summary tag.", null, null, null, null, false, null, null, null)); + cache.Add(@"P:XmlPropertyTestClass.ValueOnly", new XmlComment(null, null, null, null, @"A property with only value tag.", false, null, null, null)); + cache.Add(@"P:XmlPropertyTestClass.BothSummaryAndValue", new XmlComment(@"A property with both summary and value.", null, null, null, @"Additional value information.", false, null, null, null)); + cache.Add(@"P:XmlPropertyTestClass.ReturnsOnly", new XmlComment(null, null, null, @"This should be ignored for properties.", null, false, null, null, null)); + cache.Add(@"P:XmlPropertyTestClass.SummaryAndReturns", new XmlComment(@"A property with summary and returns.", null, null, @"This should be ignored for properties.", null, false, null, null, null)); cache.Add(@"M:ExampleClass.Add(System.Int32,System.Int32)", new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@" ```int c = Math.Add(4, 5); if (c > 10) { @@ -555,11 +561,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -579,7 +594,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -636,12 +651,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -650,7 +674,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs index 419dd3c5caff..0c2836cc9f2f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs @@ -438,11 +438,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -462,7 +471,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -519,12 +528,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -533,7 +551,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs index 7ddd295b8d29..c8da228d94d8 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs @@ -89,6 +89,9 @@ private static Dictionary GenerateCacheEntries() cache.Add(@"P:XmlDocPriorityParametersClass.AllThreeProperty", new XmlComment(@"Property with all three types of documentation.", null, null, @"Returns-based description that should be overridden by value.", @"Value-based description that should take highest precedence.", false, null, null, null)); cache.Add(@"P:XmlDocPriorityParametersClass.ReturnsOnlyProperty", new XmlComment(null, null, null, @"Returns-only description.", null, false, null, null, null)); cache.Add(@"P:XmlDocPriorityParametersClass.ValueOnlyProperty", new XmlComment(null, null, null, null, @"Value-only description.", false, null, null, null)); + cache.Add(@"P:SummaryValueParametersClass.SummaryProperty", new XmlComment(@"Property with only summary documentation.", null, null, null, null, false, null, null, null)); + cache.Add(@"P:SummaryValueParametersClass.ValueProperty", new XmlComment(@"Property with summary that should be overridden by value.", null, null, null, @"Value description that should take precedence over summary.", false, null, null, null)); + cache.Add(@"P:SummaryValueParametersClass.ValueOnlyProperty", new XmlComment(null, null, null, null, @"Property with only value documentation.", false, null, null, null)); cache.Add(@"M:RouteHandlerExtensionMethods.Get", new XmlComment(@"A summary of the action.", @"A description of the action.", null, @"Returns the greeting.", null, false, null, null, null)); cache.Add(@"M:RouteHandlerExtensionMethods.Get2(System.String)", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")])); cache.Add(@"M:RouteHandlerExtensionMethods.Get3(System.String)", new XmlComment(null, null, null, @"Returns the greeting.", null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null)); @@ -115,6 +118,7 @@ private static Dictionary GenerateCacheEntries() cache.Add(@"M:RouteHandlerExtensionMethods.Post19(System.String,MixedParametersClass)", new XmlComment(@"Tests mixed regular and AsParameters with examples.", null, null, null, null, false, null, [new XmlParameterComment(@"regularParam", @"A regular parameter with documentation.", null, false), new XmlParameterComment(@"mixedParams", @"Mixed parameter class with various types.", null, false)], null)); cache.Add(@"M:RouteHandlerExtensionMethods.Get20(BindingSourceParametersClass)", new XmlComment(@"Tests AsParameters with different binding sources.", null, null, null, null, false, null, [new XmlParameterComment(@"bindingParams", @"Parameters from different sources.", null, false)], null)); cache.Add(@"M:RouteHandlerExtensionMethods.Get21(XmlDocPriorityParametersClass)", new XmlComment(@"Tests XML documentation priority order (value > returns > summary).", null, null, null, null, false, null, [new XmlParameterComment(@"priorityParams", @"Parameters demonstrating XML doc priority.", null, false)], null)); + cache.Add(@"M:RouteHandlerExtensionMethods.Get22(SummaryValueParametersClass)", new XmlComment(@"Tests summary and value documentation priority on AsParameters properties.", null, null, null, null, false, null, [new XmlParameterComment(@"summaryValueParams", @"Parameters testing summary vs value priority.", null, false)], null)); return cache; } @@ -478,11 +482,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -502,7 +515,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -559,12 +572,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -573,7 +595,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs index f97dae09882c..e192067879b7 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs @@ -464,11 +464,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -488,7 +497,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -545,12 +554,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -559,7 +577,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.XmlCommentsOnPropertiesShouldApplyToSchemaReferences#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.XmlCommentsOnPropertiesShouldApplyToSchemaReferences#OpenApiXmlCommentSupport.generated.verified.cs index c60e8c189812..62eca1ce5fa6 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.XmlCommentsOnPropertiesShouldApplyToSchemaReferences#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.XmlCommentsOnPropertiesShouldApplyToSchemaReferences#OpenApiXmlCommentSupport.generated.verified.cs @@ -443,11 +443,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -467,7 +476,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -524,12 +533,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -538,7 +556,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/XmlCommentDocumentationIdTests.CanMergeXmlCommentsWithDifferentDocumentationIdFormats#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/XmlCommentDocumentationIdTests.CanMergeXmlCommentsWithDifferentDocumentationIdFormats#OpenApiXmlCommentSupport.generated.verified.cs index 5e2aefb507a9..2bea3547fc7b 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/XmlCommentDocumentationIdTests.CanMergeXmlCommentsWithDifferentDocumentationIdFormats#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/XmlCommentDocumentationIdTests.CanMergeXmlCommentsWithDifferentDocumentationIdFormats#OpenApiXmlCommentSupport.generated.verified.cs @@ -435,11 +435,20 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyDocId), out var propertyComment)) { var parameter = operation.Parameters?.SingleOrDefault(p => p.Name == metadata.Name); + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (parameter is null) { if (operation.RequestBody is not null) { - operation.RequestBody.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + operation.RequestBody.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { var content = operation.RequestBody.Content?.Values; @@ -459,7 +468,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var targetOperationParameter = UnwrapOpenApiParameter(parameter); if (targetOperationParameter is not null) { - targetOperationParameter.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary; + targetOperationParameter.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { targetOperationParameter.Example = jsonString.Parse(); @@ -516,12 +525,21 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext // Apply comments from the property if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment)) { + var description = propertyComment.Summary; + if (!string.IsNullOrEmpty(description) && !string.IsNullOrEmpty(propertyComment.Value)) + { + description = $"{description}\n{propertyComment.Value}"; + } + else if (string.IsNullOrEmpty(description)) + { + description = propertyComment.Value; + } if (schema.Metadata is null || !schema.Metadata.TryGetValue("x-schema-id", out var schemaId) || string.IsNullOrEmpty(schemaId as string)) { // Inlined schema - schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + schema.Description = description; if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Example = jsonString.Parse(); @@ -530,7 +548,10 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext else { // Schema Reference - schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary!; + if (!string.IsNullOrEmpty(description)) + { + schema.Metadata["x-ref-description"] = description; + } if (propertyComment.Examples?.FirstOrDefault() is { } jsonString) { schema.Metadata["x-ref-example"] = jsonString.Parse()!; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_0/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_0/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt index 9eed2206b116..d2b295c23ab6 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_0/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_0/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt @@ -341,7 +341,7 @@ }, "description": { "type": "string", - "description": "Another description of the todo." + "description": "A description of the the todo.\nAnother description of the todo." } } }, diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_1/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_1/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt index 4a8829575928..7a4b01a03316 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_1/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_1/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=xml.verified.txt @@ -341,7 +341,7 @@ }, "description": { "type": "string", - "description": "Another description of the todo." + "description": "A description of the the todo.\nAnother description of the todo." } } }, diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt index 4094879f54a8..a5abfc5f0433 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt @@ -1845,7 +1845,7 @@ }, "description": { "type": "string", - "description": "Another description of the todo." + "description": "A description of the the todo.\nAnother description of the todo." } } },