Skip to content
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

[Feature request]: Document specific SwaggerGenOptions #2995

Open
LennardF1989 opened this issue Jul 26, 2024 · 0 comments
Open

[Feature request]: Document specific SwaggerGenOptions #2995

LennardF1989 opened this issue Jul 26, 2024 · 0 comments

Comments

@LennardF1989
Copy link

LennardF1989 commented Jul 26, 2024

Is your feature request related to a specific problem? Or an existing feature?

If you extend existing solutions that come with a preconfigured Swashbuckle (for example Umbraco), you may want to add your own documents for your own API's, but not wish to inherit the existing configuration (eg. UseOneOfForPolymorphism or existing Filters).

Describe the solution you'd like

Currently, there can only ever be one SwaggerGenOptions, and it's split into a SwaggerGeneratorOptions and SchemaGeneratorOptions during startup using ConfigureSwaggerGeneratorOptions and ConfigureSchemaGeneratorOptions.

As such, the SwaggerGenerator and SchemaGenerator will use the same configuration for all documents. While you can make Filters opt-in by checking the documentName before proceeding with the logic, it's pretty much impossible to do something as simple as enabling/disabling UseOneOfForPolymorphism for one specific document without reinventing the wheel quite a bit.

My proposed solution is making the default ISwaggerProvider/IAsyncSwaggerProvider "document-aware". If the DI has a specific configuration for the string documentName, that one is used instead of the "global" one.

I made a proof of concept for this by creating a custom DocumentAwareSwaggerGenerator with this particular logic:

private SwaggerGenerator GetSwaggerGenerator(string documentName)
{
    var swaggerGenOptions = _serviceProvider
        .GetKeyedService<SwaggerGenOptions>(documentName);

    SwaggerGeneratorOptions swaggerGeneratorOptions;
    SchemaGeneratorOptions schemaGeneratorOptions;

    if (swaggerGenOptions == null)
    {
        swaggerGeneratorOptions = _swaggerGeneratorOptions;
        schemaGeneratorOptions = _schemaGeneratorOptions;
    }
    else
    {
        swaggerGeneratorOptions = swaggerGenOptions.SwaggerGeneratorOptions;
        schemaGeneratorOptions = swaggerGenOptions.SchemaGeneratorOptions;
    }

    var schemaGenerator = new SchemaGenerator(schemaGeneratorOptions, _serializerDataContractResolver);

    return new SwaggerGenerator(swaggerGeneratorOptions, _apiDescriptionsProvider, schemaGenerator);
}

I then added an Extension method:

        public static IServiceCollection ConfigureSwaggerGen<T>(
            this IServiceCollection services,
            string name,
            OpenApiInfo info
        )
            where T : class, IConfigureOptions<SwaggerGenOptions>
        {
            services.Configure<SwaggerGenOptions>(options =>
            {
                options.SwaggerDoc(name, info);
            });

            //NOTE: Transient so the constructor can inject all types of scope
            services.AddKeyedTransient<IConfigureOptions<SwaggerGenOptions>, T>(name);

            //NOTE: Singleton so the factory is only run once
            services.AddKeyedSingleton(name, (provider, _) =>
            {
                var swaggerGenOptions = new SwaggerGenOptions();

                swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs.Add(
                    name,
                    info
                );

                provider
                    .GetRequiredKeyedService<IConfigureOptions<SwaggerGenOptions>>(name)
                    .Configure(swaggerGenOptions);

                //Snip: ConfigureSwaggerGeneratorOptions CreateFilter logic
                //Snip: ConfigureSchemaGeneratorOptions CreateFilter logic

                return swaggerGenOptions;
            });

            return services;
        }

All-in-all, this works! But I feel something like this should be included as part of Swashbuckle by default.

Additional context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant