Skip to content

Commit

Permalink
Fix that XML comment examples do not show up if the type is string an…
Browse files Browse the repository at this point in the history
…d the example contains quotation marks

domaindrivendev#2088
  • Loading branch information
Daniel committed Oct 13, 2023
1 parent 8f363f7 commit 618102c
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 35 deletions.
@@ -0,0 +1,27 @@
using System.Text.Json;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
public static class XmlCommentsExampleHelper
{
public static IOpenApiAny Create(
SchemaRepository schemaRepository,
OpenApiSchema schema,
string exampleString)
{
var isStringType =
(schema.ResolveType(schemaRepository) == "string") &&
!exampleString.Equals("null");

var exampleAsJson = isStringType
? JsonSerializer.Serialize(exampleString)
: exampleString;

var example = OpenApiAnyFactory.CreateFromJson(exampleAsJson);

return example;
}
}
}
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.ToString());
}
}
}
Expand Down
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
Expand Up @@ -48,11 +48,7 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context)
var exampleNode = fieldOrPropertyNode.SelectSingleNode("example");
if (exampleNode != null)
{
var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == "string") && !exampleNode.Value.Equals("null")
? $"\"{exampleNode.ToString()}\""
: exampleNode.ToString();

schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson);
schema.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, schema, exampleNode.ToString());
}
}
}
Expand Down
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
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,54 @@
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()
{
OpenApiSchema schema = new OpenApiSchema();

IOpenApiAny example = XmlCommentsExampleHelper.Create(
schemaRepository,
schema,
"[\"one\",\"two\",\"three\"]");


var output = (OpenApiArray)example;
Assert.Equal(3, output.Count);
Assert.Equal("one", ((OpenApiString)output[0]).Value);
Assert.Equal("two", ((OpenApiString)output[1]).Value);
Assert.Equal("three", ((OpenApiString)output[2]).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.Equal(((OpenApiString)example).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.Equal(AnyType.Null, ((OpenApiNull)example).AnyType);
}
}
}
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
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 618102c

Please sign in to comment.