TCH
TCH

Reputation: 594

Configuration Swagger Spring Boot 2.7 + Jersey

I configured a swagger for a private API on a spring boot application. These endpoints are made with spring.
There is also a public API on the same server with endpoints made with Jersey.
I configured a swagger for this public API and it seems to work.
When I access to http://localhost:8080/@context.root@/swagger-ui/index.html I can see definitions

However I feel like the configuration is a bit "weird" and could be improved.
Indeed the openapi.json files are created in two different ways and I am pretty sure the swagger configuration could be the same for both API.
I would like to configure the swagger for the jersey resources (public API) using GroupedOpenApi class and remove all urls configuration from application.yml, but so far unsuccessfuly...

spring boot version: 2.7.0 but it is planned to upgrade to v3 next month

pom.xml

<dependency>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-jaxrs2</artifactId>
    <version>2.1.2</version>
</dependency>

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.2</version>
</dependency>

SwaggerConfiguration.java

Configure swagger "02 - private" for private api made with spring endpoints

import java.time.LocalDate;
import java.time.LocalDateTime;

import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.SpringDocUtils;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.servers.Server;

@Configuration
public class SwaggerConfiguration implements WebMvcConfigurer {

    private static final String FORWARD_SWAGGER_UI_HTML = "forward:/swagger-ui.html";

    @Override
    public void addViewControllers(final ViewControllerRegistry registry) {
        registry.addViewController("/swagger").setViewName(FORWARD_SWAGGER_UI_HTML);
        registry.addViewController("/swagger/").setViewName(FORWARD_SWAGGER_UI_HTML);
        registry.addViewController("/swagger-ui").setViewName(FORWARD_SWAGGER_UI_HTML);
        registry.addViewController("/swagger-ui/").setViewName(FORWARD_SWAGGER_UI_HTML);
    }

    @Bean
    public OpenAPI openApiDefinition(final AppProperties properties) {
        SpringDocUtils.getConfig().replaceWithClass(LocalDate.class, String.class);
        SpringDocUtils.getConfig().replaceWithClass(LocalDateTime.class, String.class);

        return new OpenAPI()
            .addServersItem(new Server().url("/" + properties.getContextRoot()))
            .info(new Info().title(properties.getAppName()));
    }

    @Bean
    public GroupedOpenApi privateApi(final AppProperties properties) {
        return GroupedOpenApi.builder()
            .group("02 - private")
            .pathsToMatch("/api/**")
            .pathsToExclude("/api/pub/*")
            .addOpenApiCustomiser(this.customizeApiPrivate(properties))
            .build();
    }

    private OpenApiCustomiser customizeApiPrivate(final AppProperties properties) {
        return (final OpenAPI openApi) -> openApi.getInfo()
            .title("Private API title")
            .description("Private API description")
            .version(properties.getAppVersion());
    }

    // This create "01 - public" but with empty endpoints / operations, 
    // it seems like jersey endpoints are just ignored if swagger is configured this way
    /**
    @Bean
    public GroupedOpenApi publicApi(final AppProperties properties) {
        return GroupedOpenApi.builder()
            .group("01 - public")
            .pathsToMatch("/api/pub/**")
            .packagesToScan("path.to.resources.package.api.pub")
            .addOpenApiCustomiser(...)
            .build();
    }
    */
}

application.yml

Configure location of openapi.json for the public api "01 - public"

springdoc:
  swagger-ui:
    disable-swagger-default-url: true
    defaultModelExpandDepth: 3
    operationsSorter: "alpha"
    tagsSorter: "alpha"
    urls:
      - name: '01 - public'
        url: '/@context.root@/swagger-ui/pub/openapi.json'

AppConfiguration.java

Configure jersey endpoints + add mapping for swagger of the public api

@EnableWebMvc
@Configuration
@PropertySources(...)
@ComponentScan(...)
@ConfigurationPropertiesScan(...)
public class AppConfiguration implements WebMvcConfigurer {

    @Bean
    public ServletRegistrationBean<ServletContainer> publicJersey(AppProperties properties) 
    {
        ServletRegistrationBean<ServletContainer> pubJersey = new ServletRegistrationBean<>(
             new ServletContainer(new ApiPublicJerseyConfig(properties)));
        pubJersey.addUrlMappings("/api/pub/*","/swagger-ui/pub/*");
        pubJersey.setLoadOnStartup(0);
        return pubJersey;
    }
    ...
}

ApiPublicJerseyConfig.java

Configure swagger "01 - public" for public api made with jersey endpoints

import org.glassfish.jersey.server.ResourceConfig;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource;
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
import io.swagger.v3.oas.integration.SwaggerConfiguration;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.servers.Server;

public class ApiPublicJerseyConfig extends ResourceConfig {

    public ApiPublicJerseyConfig(AppProperties properties) {
        super();
        packages("path.to.resources.package.api.pub");
        register(swaggerConfig(properties));
    }

    private static BaseOpenApiResource swaggerConfig(AppProperties properties) {
        return new OpenApiResource().openApiConfiguration(
            new SwaggerConfiguration()
                .resourcePackages(new HashSet<>(Collections.singletonList("path.to.resources.package.api.pub")))
                .openAPI(publicOpenApi(properties))
        );
    }

    private static OpenAPI publicOpenApi(final AppProperties properties) {
        return new OpenAPI()
            .addServersItem(new Server().url("/" + properties.getContextRoot() + "/api/pub"))
            .info(new Info()
                .title("Public API title")
                .description("Public API description")
                .version(properties.getAppVersion())
            );
    }
}

Upvotes: 0

Views: 1471

Answers (1)

TCH
TCH

Reputation: 594

Ok I think I had my answer here https://springdoc.org/faq.html It seems like I cannot have the same kind of swagger configuration for both servlet types (Spring & Jersey)

Does springdoc-openapi support Jersey?

If you are using JAX-RS and as implementation Jersey (@Path for example), we do not support it.

We only support exposing Rest Endpoints using Spring managed beans (@RestController for example).

You can have a look at swagger-jaxrs2 project:

https://github.com/swagger-api/swagger-samples/tree/2.0/java/java-jersey2-minimal

Upvotes: 1

Related Questions