Skip to content

Commit

Permalink
Fix XML comment examples where example contains quotes (#2727)
Browse files Browse the repository at this point in the history
Fix that XML comment examples do not show up if the type is string and the example contains quotation marks.
  • Loading branch information
dldl-cmd committed May 6, 2024
1 parent 42e6876 commit 8cc6870
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class GenericControllerWithXmlComments<T>
public void ActionWithSummaryAndResponseTags(T param)
{ }

/// <param name="param1" example="Example for param1">Description for param1</param>
/// <param name="param2" example="Example for param2">Description for param2</param>
/// <param name="param1" example="Example for &quot;param1&quot;">Description for param1</param>
/// <param name="param2" example="Example for &quot;param2&quot;">Description for param2</param>
public void ActionWithParamTags(T param1, T param2)
{ }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FakeControllerWithXmlComments
public void ActionWithSummaryAndRemarksTags()
{ }

/// <param name="param1" example="Example for param1">Description for param1</param>
/// <param name="param1" example="Example for &quot;param1&quot;">Description for param1</param>
/// <param name="param2" example="http://test.com/?param1=1&amp;param2=2">Description for param2</param>
public void ActionWithParamTags(string param1, string param2)
{ }
Expand Down
Original file line number Diff line number Diff line change
@@ -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<OpenApiArray>(example);
Assert.Equal(3, actual.Count);

var item1 = Assert.IsType<OpenApiString>(actual[0]);
var item2 = Assert.IsType<OpenApiString>(actual[1]);
var item3 = Assert.IsType<OpenApiString>(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<OpenApiString>(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<OpenApiNull>(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<OpenApiArray>(example);
Assert.Empty(actual);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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]
Expand Down

0 comments on commit 8cc6870

Please sign in to comment.