diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs new file mode 100644 index 0000000000..40a2271723 --- /dev/null +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; + +namespace Swashbuckle.AspNetCore.SwaggerGen +{ + internal static class XmlCommentsExampleHelper + { + public static IOpenApiAny Create( + SchemaRepository schemaRepository, + OpenApiSchema schema, + string exampleString) + { + var isStringType = + (schema?.ResolveType(schemaRepository) == "string") && + !string.Equals(exampleString, "null"); + + var exampleAsJson = isStringType + ? JsonSerializer.Serialize(exampleString) + : exampleString; + + var example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + + return example; + } + } +} diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs index 58e00c31c8..6eb17fa94d 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs @@ -42,11 +42,7 @@ private void ApplyPropertyTags(OpenApiParameter parameter, ParameterFilterContex var exampleNode = propertyNode.SelectSingleNode("example"); if (exampleNode == null) return; - var exampleAsJson = (parameter.Schema?.ResolveType(context.SchemaRepository) == "string") - ? $"\"{exampleNode.ToString()}\"" - : exampleNode.ToString(); - - parameter.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + parameter.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, exampleNode.ToString()); } private void ApplyParamTags(OpenApiParameter parameter, ParameterFilterContext context) @@ -71,11 +67,7 @@ private void ApplyParamTags(OpenApiParameter parameter, ParameterFilterContext c var example = paramNode.GetAttribute("example", ""); if (string.IsNullOrEmpty(example)) return; - var exampleAsJson = (parameter.Schema?.ResolveType(context.SchemaRepository) == "string") - ? $"\"{example}\"" - : example; - - parameter.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + parameter.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, example); } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs index 5640cbda42..dcd4f96267 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs @@ -50,11 +50,7 @@ private void ApplyPropertyTags(OpenApiRequestBody requestBody, RequestBodyFilter foreach (var mediaType in requestBody.Content.Values) { - var exampleAsJson = (mediaType.Schema?.ResolveType(context.SchemaRepository) == "string") - ? $"\"{exampleNode.ToString()}\"" - : exampleNode.ToString(); - - mediaType.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + mediaType.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, mediaType.Schema, exampleNode.ToString()); } } @@ -82,11 +78,7 @@ private void ApplyParamTags(OpenApiRequestBody requestBody, RequestBodyFilterCon foreach (var mediaType in requestBody.Content.Values) { - var exampleAsJson = (mediaType.Schema?.ResolveType(context.SchemaRepository) == "string") - ? $"\"{example}\"" - : example; - - mediaType.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + mediaType.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, mediaType.Schema, example); } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs index ddf25405e2..4075034e27 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs @@ -69,11 +69,7 @@ private static void TrySetExample(OpenApiSchema schema, SchemaFilterContext cont if (example == null) return; - var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == "string") && !example.Equals("null") - ? $"\"{example}\"" - : example; - - schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson); + schema.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, schema, example); } } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeConstructedControllerWithXmlComments.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeConstructedControllerWithXmlComments.cs index 9c331aa73f..f759e38c2a 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeConstructedControllerWithXmlComments.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeConstructedControllerWithXmlComments.cs @@ -17,8 +17,8 @@ public class GenericControllerWithXmlComments public void ActionWithSummaryAndResponseTags(T param) { } - /// Description for param1 - /// Description for param2 + /// Description for param1 + /// Description for param2 public void ActionWithParamTags(T param1, T param2) { } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeControllerWithXmlComments.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeControllerWithXmlComments.cs index 6d9cd92377..431b33333c 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeControllerWithXmlComments.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeControllerWithXmlComments.cs @@ -18,7 +18,7 @@ public class FakeControllerWithXmlComments public void ActionWithSummaryAndRemarksTags() { } - /// Description for param1 + /// Description for param1 /// Description for param2 public void ActionWithParamTags(string param1, string param2) { } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs new file mode 100644 index 0000000000..a9b7cfd6b6 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs @@ -0,0 +1,74 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Xunit; + +namespace Swashbuckle.AspNetCore.SwaggerGen.Test +{ + public class XmlCommentsExampleHelperTests + { + private readonly SchemaRepository schemaRepository = new SchemaRepository(); + + [Fact] + public void Create_BuildsOpenApiArrayJson__When_NotStringTypeAndDataIsArray() + { + var schema = new OpenApiSchema(); + + IOpenApiAny example = XmlCommentsExampleHelper.Create( + schemaRepository, + schema, + "[\"one\",\"two\",\"three\"]"); + + Assert.NotNull(example); + var actual = Assert.IsType(example); + Assert.Equal(3, actual.Count); + + var item1 = Assert.IsType(actual[0]); + var item2 = Assert.IsType(actual[1]); + var item3 = Assert.IsType(actual[2]); + Assert.Equal("one", item1.Value); + Assert.Equal("two", item2.Value); + Assert.Equal("three", item3.Value); + } + + [Fact] + public void Create_BuildsOpenApiString_When_TypeString() + { + string exampleString = "example string with special characters\"<>\r\n\""; + OpenApiSchema schema = new OpenApiSchema { Type = "string" }; + schemaRepository.AddDefinition("test", schema); + + IOpenApiAny example = XmlCommentsExampleHelper.Create( + schemaRepository, schema, exampleString); + + Assert.NotNull(example); + var actual = Assert.IsType(example); + Assert.Equal(actual.Value, exampleString); + } + + [Fact] + public void Create_ReturnsNull_When_TypeString_and_ValueNull() + { + OpenApiSchema schema = new OpenApiSchema { Type = "string" }; + schemaRepository.AddDefinition("test", schema); + + IOpenApiAny example = XmlCommentsExampleHelper.Create( + schemaRepository, schema, "null"); + + Assert.NotNull(example); + var actual = Assert.IsType(example); + Assert.Equal(AnyType.Null, actual.AnyType); + } + + [Fact] + public void Create_AllowsSchemaToBeNull() + { + OpenApiSchema schema = null; + + IOpenApiAny example = XmlCommentsExampleHelper.Create(schemaRepository, schema, "[]"); + + Assert.NotNull(example); + var actual = Assert.IsType(example); + Assert.Empty(actual); + } + } +} diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs index f27613e293..77502b15ab 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs @@ -23,7 +23,7 @@ public void Apply_SetsDescriptionAndExample_FromActionParamTag() Assert.Equal("Description for param1", parameter.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"Example for param1\"", parameter.Example.ToJson()); + Assert.Equal("\"Example for \\\"param1\\\"\"", parameter.Example.ToJson()); } [Fact] @@ -57,7 +57,7 @@ public void Apply_SetsDescriptionAndExample_FromUnderlyingGenericTypeActionParam Assert.Equal("Description for param1", parameter.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"Example for param1\"", parameter.Example.ToJson()); + Assert.Equal("\"Example for \\\"param1\\\"\"", parameter.Example.ToJson()); } [Fact] diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs index 73995e32da..9d4bd7dabf 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs @@ -34,7 +34,7 @@ public void Apply_SetsDescriptionAndExample_FromActionParamTag() Assert.Equal("Description for param1", requestBody.Description); Assert.NotNull(requestBody.Content["application/json"].Example); - Assert.Equal("\"Example for param1\"", requestBody.Content["application/json"].Example.ToJson()); + Assert.Equal("\"Example for \\\"param1\\\"\"", requestBody.Content["application/json"].Example.ToJson()); } [Fact] @@ -60,7 +60,7 @@ public void Apply_SetsDescriptionAndExample_FromUnderlyingGenericTypeActionParam Assert.Equal("Description for param1", requestBody.Description); Assert.NotNull(requestBody.Content["application/json"].Example); - Assert.Equal("\"Example for param1\"", requestBody.Content["application/json"].Example.ToJson()); + Assert.Equal("\"Example for \\\"param1\\\"\"", requestBody.Content["application/json"].Example.ToJson()); } [Fact]