From 745db4e47e7d8f38cf38bf48f36ab3031ef1e840 Mon Sep 17 00:00:00 2001 From: pixellos Date: Sat, 12 Nov 2022 12:15:25 +0100 Subject: [PATCH 1/2] Add support for reading summary and example from default type constructor. --- .../XmlComments/XmlCommentsSchemaFilter.cs | 40 ++++++++++++++----- .../Fixtures/XmlAnnotatedRecord.cs | 33 +++++++++++++++ .../XmlCommentsSchemaFilterTests.cs | 11 +++++ 3 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs index 44fae0245e..57aaca13c5 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs @@ -39,21 +39,41 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) var fieldOrPropertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.MemberInfo); var fieldOrPropertyNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{fieldOrPropertyMemberName}']"); - if (fieldOrPropertyNode == null) return; + var recordTypeName = XmlCommentsNodeNameHelper.GetMemberNameForType(context.MemberInfo.DeclaringType); + var recordDefaultConstructorProperty = + _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{recordTypeName}']/param[@name='{context.MemberInfo.Name}']"); - var summaryNode = fieldOrPropertyNode.SelectSingleNode("summary"); - if (summaryNode != null) - schema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); + if (recordDefaultConstructorProperty != null) + { + var summaryNode = recordDefaultConstructorProperty.Value; + if (summaryNode != null) + schema.Description = XmlCommentsTextHelper.Humanize(summaryNode); + + var example = recordDefaultConstructorProperty.GetAttribute("example", string.Empty); + SetExample(schema, context, example); + } - var exampleNode = fieldOrPropertyNode.SelectSingleNode("example"); - if (exampleNode != null) + if (fieldOrPropertyNode != null) { - var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == "string") && !exampleNode.Value.Equals("null") - ? $"\"{exampleNode.ToString()}\"" - : exampleNode.ToString(); + var summaryNode = fieldOrPropertyNode.SelectSingleNode("summary"); + if (summaryNode != null) + schema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); - schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + var exampleNode = fieldOrPropertyNode.SelectSingleNode("example"); + SetExample(schema, context, exampleNode?.Value); } } + + private static void SetExample(OpenApiSchema schema, SchemaFilterContext context, string example) + { + if (example == null) + return; + + var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == "string") && !example.Equals("null") + ? $"\"{example}\"" + : example; + + schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + } } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs new file mode 100644 index 0000000000..7b35dbc335 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs @@ -0,0 +1,33 @@ +using System; +using Swashbuckle.AspNetCore.TestSupport; + +namespace Swashbuckle.AspNetCore.SwaggerGen.Test +{ + /// + /// Summary for XmlAnnotatedType + /// + /// Summary for BoolProperty + /// Summary for IntProperty + /// Summary for LongProperty + /// Summary for FloatProperty + /// Summary for DoubleProperty + /// Summary for EnumProperty + /// Summary for GuidProperty + /// Summary for Nullable StringPropertyWithNullExample + /// Summary for StringProperty + /// Summary for StringPropertyWithUri + /// Summary for ObjectProperty + public record XmlAnnotatedRecord( + bool BoolProperty, + int IntProperty, + long LongProperty, + float FloatProperty, + double DoubleProperty, + IntEnum EnumProperty, + Guid GuidProperty, + string StringPropertyWithNullExample, + string StringProperty, + string StringPropertyWithUri, + object ObjectProperty + ); +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs index 555aa3e7b9..ecfba3e080 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs @@ -68,6 +68,17 @@ public void Apply_SetsDescription_FromPropertySummaryTag( [InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.ObjectProperty), "object", "{\n \"prop1\": 1,\n \"prop2\": \"foobar\"\n}")] [InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithNullExample), "string", "null")] [InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithUri), "string", "\"https://test.com/a?b=1&c=2\"")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.BoolProperty), "boolean", "true")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.IntProperty), "integer", "10")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.LongProperty), "integer", "4294967295")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.FloatProperty), "number", "1.2")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.DoubleProperty), "number", "1.25")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.EnumProperty), "integer", "2")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.GuidProperty), "string", "\"d3966535-2637-48fa-b911-e3c27405ee09\"")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringProperty), "string", "\"Example for StringProperty\"")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.ObjectProperty), "object", "{\n \"prop1\": 1,\n \"prop2\": \"foobar\"\n}")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithNullExample), "string", "null")] + [InlineData(typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithUri), "string", "\"https://test.com/a?b=1&c=2\"")] [UseInvariantCulture] public void Apply_SetsExample_FromPropertyExampleTag( Type declaringType, From f9997fd2a0f774999d00dd1c54d0750e12342e62 Mon Sep 17 00:00:00 2001 From: pixellos Date: Sat, 13 Apr 2024 13:03:01 +0200 Subject: [PATCH 2/2] Resolve comment --- .../XmlComments/XmlCommentsSchemaFilter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs index 57aaca13c5..ddf25405e2 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs @@ -50,7 +50,7 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) schema.Description = XmlCommentsTextHelper.Humanize(summaryNode); var example = recordDefaultConstructorProperty.GetAttribute("example", string.Empty); - SetExample(schema, context, example); + TrySetExample(schema, context, example); } if (fieldOrPropertyNode != null) @@ -60,11 +60,11 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) schema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); var exampleNode = fieldOrPropertyNode.SelectSingleNode("example"); - SetExample(schema, context, exampleNode?.Value); + TrySetExample(schema, context, exampleNode?.Value); } } - private static void SetExample(OpenApiSchema schema, SchemaFilterContext context, string example) + private static void TrySetExample(OpenApiSchema schema, SchemaFilterContext context, string example) { if (example == null) return;