From 76ec39d2db343566086e29247970ca7f59017eb7 Mon Sep 17 00:00:00 2001
From: stb-co <93922970+stb-co@users.noreply.github.com>
Date: Thu, 16 May 2024 14:47:23 +0200
Subject: [PATCH] Fix schema generation for C# 9 positional record with no
example (#2901)
* Add schema filter test for when example tag is not present
* Add datetime to schema filter example tag positive test
* Fix missing example property on record xmldoc param tag causing unexpected empty example string in generated schema
---
.../XmlComments/XmlCommentsSchemaFilter.cs | 5 +-
.../Fixtures/XmlAnnotatedRecord.cs | 4 +-
.../XmlAnnotatedRecordWithoutExample.cs | 35 +++++++++
.../Fixtures/XmlAnnotatedType.cs | 6 ++
.../XmlAnnotatedTypeWithoutExample.cs | 71 +++++++++++++++++++
.../XmlCommentsSchemaFilterTests.cs | 42 +++++++++++
6 files changed, 161 insertions(+), 2 deletions(-)
create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecordWithoutExample.cs
create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedTypeWithoutExample.cs
diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs
index 4075034e27..a246657d2c 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs
@@ -50,7 +50,10 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context)
schema.Description = XmlCommentsTextHelper.Humanize(summaryNode);
var example = recordDefaultConstructorProperty.GetAttribute("example", string.Empty);
- TrySetExample(schema, context, example);
+ if (!string.IsNullOrEmpty(example))
+ {
+ TrySetExample(schema, context, example);
+ }
}
if (fieldOrPropertyNode != null)
diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs
index 7b35dbc335..c337c379ed 100644
--- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs
+++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecord.cs
@@ -4,13 +4,14 @@
namespace Swashbuckle.AspNetCore.SwaggerGen.Test
{
///
- /// Summary for XmlAnnotatedType
+ /// Summary for XmlAnnotatedRecord
///
/// Summary for BoolProperty
/// Summary for IntProperty
/// Summary for LongProperty
/// Summary for FloatProperty
/// Summary for DoubleProperty
+ /// Summary for DateTimeProperty
/// Summary for EnumProperty
/// Summary for GuidProperty
/// Summary for Nullable StringPropertyWithNullExample
@@ -23,6 +24,7 @@ public record XmlAnnotatedRecord(
long LongProperty,
float FloatProperty,
double DoubleProperty,
+ DateTime DateTimeProperty,
IntEnum EnumProperty,
Guid GuidProperty,
string StringPropertyWithNullExample,
diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecordWithoutExample.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecordWithoutExample.cs
new file mode 100644
index 0000000000..2eea51841e
--- /dev/null
+++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedRecordWithoutExample.cs
@@ -0,0 +1,35 @@
+using System;
+using Swashbuckle.AspNetCore.TestSupport;
+
+namespace Swashbuckle.AspNetCore.SwaggerGen.Test
+{
+ ///
+ /// Summary for XmlAnnotatedRecordWithoutExample
+ ///
+ /// Summary for BoolProperty
+ /// Summary for IntProperty
+ /// Summary for LongProperty
+ /// Summary for FloatProperty
+ /// Summary for DoubleProperty
+ /// Summary for DateTimeProperty
+ /// Summary for EnumProperty
+ /// Summary for GuidProperty
+ /// Summary for Nullable StringPropertyWithNullExample
+ /// Summary for StringProperty
+ /// Summary for StringPropertyWithUri
+ /// Summary for ObjectProperty
+ public record XmlAnnotatedRecordWithoutExample(
+ bool BoolProperty,
+ int IntProperty,
+ long LongProperty,
+ float FloatProperty,
+ double DoubleProperty,
+ DateTime DateTimeProperty,
+ 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/Fixtures/XmlAnnotatedType.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedType.cs
index a61b54bd72..4227d35ffd 100644
--- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedType.cs
+++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedType.cs
@@ -45,6 +45,12 @@ public class XmlAnnotatedType
/// 1.25
public double DoubleProperty { get; set; }
+ ///
+ /// Summary for DateTimeProperty
+ ///
+ /// 6/22/2022 12:00:00 AM
+ public DateTime DateTimeProperty { get; set; }
+
///
/// Summary for EnumProperty
///
diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedTypeWithoutExample.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedTypeWithoutExample.cs
new file mode 100644
index 0000000000..f06f01e840
--- /dev/null
+++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/XmlAnnotatedTypeWithoutExample.cs
@@ -0,0 +1,71 @@
+using System;
+using Swashbuckle.AspNetCore.TestSupport;
+
+namespace Swashbuckle.AspNetCore.SwaggerGen.Test
+{
+ ///
+ /// Summary for XmlAnnotatedTypeWithoutExample
+ ///
+ public class XmlAnnotatedTypeWithoutExample
+ {
+ ///
+ /// Summary for BoolProperty
+ ///
+ public bool BoolProperty { get; set; }
+
+ ///
+ /// Summary for IntProperty
+ ///
+ public int IntProperty { get; set; }
+
+ ///
+ /// Summary for LongProperty
+ ///
+ public long LongProperty { get; set; }
+
+ ///
+ /// Summary for FloatProperty
+ ///
+ public float FloatProperty { get; set; }
+
+ ///
+ /// Summary for DoubleProperty
+ ///
+ public double DoubleProperty { get; set; }
+
+ ///
+ /// Summary for DateTimeProperty
+ ///
+ public DateTime DateTimeProperty { get; set; }
+
+ ///
+ /// Summary for EnumProperty
+ ///
+ public IntEnum EnumProperty { get; set; }
+
+ ///
+ /// Summary for GuidProperty
+ ///
+ public Guid GuidProperty { get; set; }
+
+ ///
+ /// Summary for Nullable StringPropertyWithNullExample
+ ///
+ public string StringPropertyWithNullExample { get; set; }
+
+ ///
+ /// Summary for StringProperty
+ ///
+ public string StringProperty { get; set; }
+
+ ///
+ /// Summary for StringPropertyWithUri
+ ///
+ public string StringPropertyWithUri { get; set; }
+
+ ///
+ /// Summary for ObjectProperty
+ ///
+ public object ObjectProperty { get; set; }
+ }
+}
\ 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 ecfba3e080..36c32db543 100644
--- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs
+++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs
@@ -62,6 +62,7 @@ public void Apply_SetsDescription_FromPropertySummaryTag(
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.LongProperty), "integer", "4294967295")]
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.FloatProperty), "number", "1.2")]
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.DoubleProperty), "number", "1.25")]
+ [InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.DateTimeProperty), "string", "\"6/22/2022 12:00:00 AM\"")]
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.EnumProperty), "integer", "2")]
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.GuidProperty), "string", "\"d3966535-2637-48fa-b911-e3c27405ee09\"")]
[InlineData(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringProperty), "string", "\"Example for StringProperty\"")]
@@ -73,6 +74,7 @@ public void Apply_SetsDescription_FromPropertySummaryTag(
[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.DateTimeProperty), "string", "\"6/22/2022 12:00:00 AM\"")]
[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\"")]
@@ -96,6 +98,46 @@ public void Apply_SetsExample_FromPropertyExampleTag(
Assert.Equal(expectedExampleAsJson, schema.Example.ToJson());
}
+ [Theory]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.BoolProperty), "boolean")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.IntProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.LongProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.FloatProperty), "number")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.DoubleProperty), "number")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.DateTimeProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.EnumProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.GuidProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.StringProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.ObjectProperty), "object")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.StringPropertyWithNullExample), "string")]
+ [InlineData(typeof(XmlAnnotatedTypeWithoutExample), nameof(XmlAnnotatedTypeWithoutExample.StringPropertyWithUri), "string")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.BoolProperty), "boolean")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.IntProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.LongProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.FloatProperty), "number")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.DoubleProperty), "number")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.DateTimeProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.EnumProperty), "integer")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.GuidProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.StringProperty), "string")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.ObjectProperty), "object")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.StringPropertyWithNullExample), "string")]
+ [InlineData(typeof(XmlAnnotatedRecordWithoutExample), nameof(XmlAnnotatedRecordWithoutExample.StringPropertyWithUri), "string")]
+ [UseInvariantCulture]
+ public void Apply_DoesNotSetExample_WhenPropertyExampleTagIsNotProvided(
+ Type declaringType,
+ string propertyName,
+ string schemaType)
+ {
+ var propertyInfo = declaringType.GetProperty(propertyName);
+ var schema = new OpenApiSchema { Type = schemaType };
+ var filterContext = new SchemaFilterContext(propertyInfo.PropertyType, null, null, memberInfo: propertyInfo);
+
+ Subject().Apply(schema, filterContext);
+
+ Assert.Null(schema.Example);
+ }
+
[Theory]
[InlineData("en-US", 1.2F)]
[InlineData("sv-SE", 1.2F)]