-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Better support for open generic when mapping types #2704
base: master
Are you sure you want to change the base?
Conversation
/// <summary> | ||
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema | ||
/// </summary> | ||
/// <param name="swaggerGenOptions"></param> | ||
/// <param name="type">System type</param> | ||
/// <param name="schemaFactory">A factory method that generates Schema's for the provided type</param> | ||
public static void MapType( | ||
this SwaggerGenOptions swaggerGenOptions, | ||
Type type, | ||
Func<MappingContext, OpenApiSchema> schemaFactory) | ||
{ | ||
swaggerGenOptions.SchemaGeneratorOptions.CustomTypeMappings.Add(type, schemaFactory); | ||
} | ||
|
||
/// <summary> | ||
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema | ||
/// </summary> | ||
/// <typeparam name="T">System type</typeparam> | ||
/// <param name="swaggerGenOptions"></param> | ||
/// <param name="schemaFactory">A factory method that generates Schema's for the provided type</param> | ||
public static void MapType<T>( | ||
this SwaggerGenOptions swaggerGenOptions, | ||
Func<MappingContext, OpenApiSchema> schemaFactory) | ||
{ | ||
swaggerGenOptions.MapType(typeof(T), schemaFactory); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've kept the old MapType
extension methods so that existing usages of this method won't become broken as I'm now providing the MappingContext
.
However, if anyone is using swaggerGenOptions.SchemaGeneratorOptions.CustomTypeMappings
directly instead of these helper methods, they will need to update their code.
src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/MappingContext.cs
Outdated
Show resolved
Hide resolved
...Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs
Outdated
Show resolved
Hide resolved
...Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs
Outdated
Show resolved
Hide resolved
src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs
Outdated
Show resolved
Hide resolved
namespace Basic.Controllers | ||
{ | ||
/// <summary> | ||
/// Summary for GenericsController | ||
/// </summary> | ||
[Route("/generics")] | ||
[Produces("application/json")] | ||
public class GenericsController | ||
{ | ||
[HttpPost(Name = "CreateString")] | ||
public string CreateString([FromBody] GenericType<string> genericString) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
[HttpPost(Name = "CreateDateTime")] | ||
public string CreateDateTime([FromBody] GenericType<DateTime> genericObject) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is the best place to put this
But it at least needs to be integration tested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as a test would fail if this functionality got broken, it should be fine to live here. New projects are typically added when specific configurations are needed for the generation or for different types of apps.
src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/MappingContext.cs
Outdated
Show resolved
Hide resolved
throw new NotImplementedException(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test for more complex custom objects such as:
public class Foo
{
public string Bar { get; set; }
}
Thanks for contributing - if you'd like to continue with this pull request, please rebase against the default branch to pick up our new CI. |
@@ -839,6 +839,26 @@ services.AddSwaggerGen(c => | |||
}; | |||
``` | |||
|
|||
As of version v6.6.x, you can easily map generic types dynamically by accessing the underlying type at runtime. In this example, I want to treat `GenericType` as if it was the generic type `T`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to make sure the version is correct when this is released
@@ -195,6 +195,33 @@ public static void AddServer(this SwaggerGenOptions swaggerGenOptions, OpenApiSe | |||
swaggerGenOptions.MapType(typeof(T), schemaFactory); | |||
} | |||
|
|||
/// <summary> | |||
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless "JSONSchema" is a specific term I've not heard before:
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema | |
/// Provides a custom mapping, for a given type, to the Swagger-flavored JSON schema |
/// </summary> | ||
/// <param name="swaggerGenOptions"></param> | ||
/// <param name="type">System type</param> | ||
/// <param name="schemaFactory">A factory method that generates Schema's for the provided type</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// <param name="schemaFactory">A factory method that generates Schema's for the provided type</param> | |
/// <param name="schemaFactory">A factory method that generates schemas for the provided type</param> |
} | ||
|
||
/// <summary> | ||
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema | |
/// Provides a custom mapping, for a given type, to the Swagger-flavored JSON schema |
SchemaIdSelector = DefaultSchemaIdSelector; | ||
SubTypesSelector = DefaultSubTypesSelector; | ||
DiscriminatorNameSelector = DefaultDiscriminatorNameSelector; | ||
DiscriminatorValueSelector = DefaultDiscriminatorValueSelector; | ||
SchemaFilters = new List<ISchemaFilter>(); | ||
} | ||
|
||
public IDictionary<Type, Func<OpenApiSchema>> CustomTypeMappings { get; set; } | ||
public IDictionary<Type, Func<MappingContext, OpenApiSchema>> CustomTypeMappings { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a binary-breaking change, so if we took it as-is this would have to be part of v7.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would get released sooner if there were a way to implement this without breaking changes.
UnderlyingType = underlyingType; | ||
} | ||
|
||
public readonly ISchemaGenerator SchemaGenerator; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use properties, not fields.
|
||
namespace Swashbuckle.AspNetCore.SwaggerGen | ||
{ | ||
public class MappingContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be sealed
.
|
||
namespace Swashbuckle.AspNetCore.SwaggerGen | ||
{ | ||
public class MappingContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please /// document all the members as this is a new public-facing type.
if (genericTypeArgument == typeof(int)) | ||
return new OpenApiSchema { Type = "number" }; | ||
|
||
throw new NotImplementedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to fail with an assertion related to what unexpected value was received.
namespace Basic.Controllers | ||
{ | ||
/// <summary> | ||
/// Summary for GenericsController | ||
/// </summary> | ||
[Route("/generics")] | ||
[Produces("application/json")] | ||
public class GenericsController | ||
{ | ||
[HttpPost(Name = "CreateString")] | ||
public string CreateString([FromBody] GenericType<string> genericString) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
[HttpPost(Name = "CreateDateTime")] | ||
public string CreateDateTime([FromBody] GenericType<DateTime> genericObject) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as a test would fail if this functionality got broken, it should be fine to live here. New projects are typically added when specific configurations are needed for the generation or for different types of apps.
@@ -55,6 +56,13 @@ public void ConfigureServices(IServiceCollection services) | |||
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Basic.xml")); | |||
|
|||
c.EnableAnnotations(); | |||
|
|||
c.MapType(typeof(GenericType<>), mappingContext => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test a generic type with more than one type parameter?
Issues
This helps with these issues:
The problem
The issue I'm trying to solve is that there's no way to generically solve all mappings for a type that uses open generics. We can currently do something like this:
But writing an exhaustive list is impossible, especially when you need to support custom objects.
The solution
Provide the user with a
MappingContext
so they can dynamically handle different types:The
MappingContext
contains these useful properties: