Skip to content

Commit

Permalink
Types with TryParse must be set with type string (#3108)
Browse files Browse the repository at this point in the history
  • Loading branch information
jgarciadelanoceda authored Oct 11, 2024
1 parent a42d8c9 commit 349ba38
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -590,14 +590,14 @@ private OpenApiParameter GenerateParameterWithoutFilter(

var isRequired = apiParameter.IsRequiredParameter();

var type = apiParameter.Type;
var type = apiParameter.ModelMetadata?.ModelType;

if (type is not null
&& type == typeof(string)
&& apiParameter.ModelMetadata?.ModelType is not null
&& apiParameter.ModelMetadata.ModelType != type)
&& apiParameter.Type is not null
&& (Nullable.GetUnderlyingType(apiParameter.Type) ?? apiParameter.Type).IsEnum)
{
type = apiParameter.ModelMetadata.ModelType;
type = apiParameter.Type;
}

var schema = (type != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,35 @@
}
}
},
"/TypeWithTryParse/{tryParse}": {
"get": {
"tags": [
"WebApi"
],
"parameters": [
{
"name": "tryParse",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/WithOpenApi/weatherforecast": {
"get": {
"tags": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,35 @@
}
}
},
"/TypeWithTryParse/{tryParse}": {
"get": {
"tags": [
"WebApi"
],
"parameters": [
{
"name": "tryParse",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/WithOpenApi/weatherforecast": {
"get": {
"tags": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures;
using Swashbuckle.AspNetCore.TestSupport;

namespace Swashbuckle.AspNetCore.SwaggerGen.Test
{
Expand Down Expand Up @@ -109,5 +110,7 @@ public void ActionHavingFromFormAttributeButNotWithIFormFile([FromForm] string p
{ }
public void ActionHavingFromFormAttributeWithSwaggerIgnore([FromForm] SwaggerIngoreAnnotatedType param1)
{ }
public void ActionHavingEnum(IntEnum param1)
{ }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,38 @@ public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_With_SwaggerIgn
Assert.Equal(new[] { nameof(SwaggerIngoreAnnotatedType.NotIgnoredString) }, mediaType.Encoding.Keys);
}

[Fact]
public void GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString()
{
var subject = Subject(
apiDescriptions:
[
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingEnum),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
parameterDescriptions:
[
new ApiParameterDescription
{
Name = "param1",
Source = BindingSource.Query,
Type = typeof(IntEnum),
ModelMetadata = ModelMetadataFactory.CreateForType(typeof(string))
}
])
]
);
var document = subject.GetSwagger("v1");

var operation = document.Paths["/resource"].Operations[OperationType.Post];
Assert.Equal("param1", operation.Parameters[0].Name);
Assert.NotNull(operation.Parameters[0].Schema);
Assert.NotNull(operation.Parameters[0].Schema.Reference);
Assert.Equal(typeof(IntEnum).Name, operation.Parameters[0].Schema.Reference.Id);
}

[Fact]
public void GetSwagger_Copies_Description_From_GeneratedSchema()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
Info: {
Title: Test API,
Version: V1
},
Paths: {
/resource: {
Operations: {
Post: {
Tags: [
{
Name: Fake,
UnresolvedReference: false
}
],
Parameters: [
{
UnresolvedReference: false,
Name: param1,
In: Query,
Required: false,
Deprecated: false,
AllowEmptyValue: false,
Style: Form,
Explode: true,
AllowReserved: false,
Schema: {
ReadOnly: false,
WriteOnly: false,
AdditionalPropertiesAllowed: true,
Nullable: false,
Deprecated: false,
UnresolvedReference: false,
Reference: {
IsFragrament: false,
Type: Schema,
Id: IntEnum,
IsExternal: false,
IsLocal: true,
ReferenceV3: #/components/schemas/IntEnum,
ReferenceV2: #/definitions/IntEnum
}
}
}
],
Responses: {
200: {
Description: OK,
UnresolvedReference: false
}
},
Deprecated: false
}
},
UnresolvedReference: false
}
},
Components: {
Schemas: {
IntEnum: {
Type: integer,
Format: int32,
ReadOnly: false,
WriteOnly: false,
AdditionalPropertiesAllowed: true,
Enum: [
{
Value: 2
},
{
Value: 4
},
{
Value: 8
}
],
Nullable: false,
Deprecated: false,
UnresolvedReference: false
}
}
},
HashCode: F2B8FE01A8D273C628EBD9F65C7F1E80622E3C5CBBB8F150E247A34C4558F617F1D056F7B1113ABE978F9E433A9FBCA1C65A6387FB918AF00AFB54E6F5A47C5D
}
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,35 @@ public Task ActionHavingFromFormAttributeWithSwaggerIgnore()
return Verifier.Verify(document);
}

[Fact]
public Task GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString()
{
var subject = Subject(
apiDescriptions:
[
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingEnum),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
parameterDescriptions:
[
new ApiParameterDescription
{
Name = "param1",
Source = BindingSource.Query,
Type = typeof(IntEnum),
ModelMetadata = ModelMetadataFactory.CreateForType(typeof(string))
}
])
]
);

var document = subject.GetSwagger("v1");

return Verifier.Verify(document);
}

[Fact]
public Task GetSwagger_Copies_Description_From_GeneratedSchema()
{
Expand Down
19 changes: 19 additions & 0 deletions test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public static IEndpointRouteBuilder MapWithOpenApiEndpoints(this IEndpointRouteB
return $"{file.FileName}{tags}";
}).WithOpenApi();

app.MapGet("/TypeWithTryParse/{tryParse}", (TypeWithTryParse tryParse) =>
{
return tryParse.Name;
}).WithOpenApi();

return app;
}
}
Expand All @@ -82,4 +87,18 @@ record class Person(string FirstName, string LastName);
record class Address(string Street, string City, string State, string ZipCode);
sealed record OrganizationCustomExchangeRatesDto([property: JsonRequired] CurrenciesRate[] CurrenciesRates);
sealed record CurrenciesRate([property: JsonRequired] string CurrencyFrom, [property: JsonRequired] string CurrencyTo, double Rate);
record TypeWithTryParse(string Name)
{
public static bool TryParse(string value, out TypeWithTryParse? result)
{
if (value is null)
{
result = null;
return false;
}

result = new TypeWithTryParse(value);
return true;
}
}
}

0 comments on commit 349ba38

Please sign in to comment.