Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.
This repository was archived by the owner on May 14, 2025. It is now read-only.

Publishing of the Backend-API description via SwaggerUI (solution incl.) #4820

Closed
@klopfdreh

Description

@klopfdreh

Problem description:
It would be nice to have SwaggerUI available in SCDF Server.

Solution description:
There are only a small amount of adjustments required to publish the SwaggerUI:

pom.xml (Include spring.factories file and add dependency - version might be added in spring-cloud-dataflow-parent):

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

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.factories</include>
                </includes>
            </resource>
        ....
    </build

Additions in DataflowOAuthSecurityConfiguration

    private static final String SWAGGER_UI_CONTEXT = "/swagger-ui/**";

    @Value("${springdoc.api-docs.path:/v3/api-docs}")
    private String apiDocsPath;

    @Value("${springdoc.swagger-ui.path:/swagger-ui.html}")
    private String swaggerUiPath;

    @Value("${springdoc.webjars.prefix:/webjars}")
    private String webjarsPrefix;

    @Value("${springdoc.swagger-ui.configUrl:/v3/api-docs/swagger-config}")
    private String swaggerUiConfig;

    @Value("${springdoc.swagger-ui.validatorUrl:validator.swagger.io/validator}")
    private String swaggerUiValidatorUrl;

    @Value("${springdoc.swagger-ui.oauth2RedirectUrl:/swagger-ui/oauth2-redirect.html}")
    private String swaggerUiOAuth2RedirectUrl;

    @Override
    public void configure(WebSecurity web) {
        String apiDocsPathContext = StringUtils.substringBeforeLast(apiDocsPath, "/");
        web.ignoring().antMatchers(
                SWAGGER_UI_CONTEXT,
                swaggerUiPath,
                webjarsPrefix,
                webjarsPrefix + "/**",
                swaggerUiConfig,
                swaggerUiValidatorUrl,
                swaggerUiOAuth2RedirectUrl,
                apiDocsPathContext + "/**");
    }

New class DataFlowSwaggerApiDocsJsonDecodeFilter (Required because of issue springdoc/springdoc-openapi#624 (comment))

@Component
@Order
public class DataFlowSwaggerApiDocsJsonDecodeFilter implements Filter {

    private static final Logger LOG = LoggerFactory.getLogger(DataFlowSwaggerApiDocsJsonDecodeFilter.class);

    @Value("${springdoc.api-docs.path:/v3/api-docs}")
    private String apiDocsPath;


    @Value("${springdoc.swagger-ui.configUrl:/v3/api-docs/swagger-config}")
    private String swaggerUiConfig;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequestWrapper httpServletRequestWrapper = new HttpServletRequestWrapper((HttpServletRequest) request);
        final ContentCachingResponseWrapper httpServletResponseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);

        String apiDocsPathContext = StringUtils.substringBeforeLast(apiDocsPath, "/");
        String swaggerUiConfigContext = StringUtils.substringBeforeLast(swaggerUiConfig, "/");
        if (httpServletRequestWrapper.getServletPath().startsWith(apiDocsPathContext) ||
                httpServletRequestWrapper.getServletPath().startsWith(swaggerUiConfigContext)) {
            // if api-docs path is requested, use wrapper classes, so that the body gets cached.
            chain.doFilter(httpServletRequestWrapper, httpServletResponseWrapper);

            ServletOutputStream outputStream = httpServletResponseWrapper.getResponse().getOutputStream();

            LOG.debug("Request for Swagger api-docs detected - unescaping json content.");
            String content = new String(httpServletResponseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
            content = StringUtils.stripStart(content, "\"");
            content = StringUtils.stripEnd(content, "\"");
            if (LOG.isTraceEnabled()) {
                LOG.trace("Using decoded JSON for serving api-docs: {}", content);
            }
            outputStream.write(StringEscapeUtils.unescapeJson(content).getBytes(StandardCharsets.UTF_8));
        } else {
            // all other scdf related api calls do nothing.
            chain.doFilter(request, response);
        }
    }
}

New class DataFlowSwaggerApiEnvironmentPostProcessor (So that Swagger and Springdoc are disabled by default)

public class DataFlowSwaggerApiEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static final String SPRINGDOC_API_DOCS_ENABLED_KEY = "springdoc.api-docs.enabled";
    private static final String SWAGGER_UI_ENABLED_KEY = "springdoc.swagger-ui.enabled";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        Optional<Object> apiDocsEnabledOptional = environment.getPropertySources().stream()
                .filter(p -> p.containsProperty(SPRINGDOC_API_DOCS_ENABLED_KEY))
                .map(p -> p.getProperty(SPRINGDOC_API_DOCS_ENABLED_KEY))
                .findAny();
        // Apply default properties
        if (!apiDocsEnabledOptional.isPresent()) {
            applyDisableFeaturePropertySources(environment, "apiDocsDisabled", SPRINGDOC_API_DOCS_ENABLED_KEY);
        }

        Optional<Object> swaggerUiEnabledOptional = environment.getPropertySources().stream()
                .filter(p -> p.containsProperty(SWAGGER_UI_ENABLED_KEY))
                .map(p -> p.getProperty(SWAGGER_UI_ENABLED_KEY))
                .findAny();
        if (!(swaggerUiEnabledOptional.isPresent())) {
            applyDisableFeaturePropertySources(environment, "swaggerUiDisabled", SWAGGER_UI_ENABLED_KEY);
        }
    }

    private void applyDisableFeaturePropertySources(ConfigurableEnvironment environment, String name, String key) {
        Properties properties = new Properties();
        properties.setProperty(key, "false");
        PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(name, properties);
        environment.getPropertySources().addLast(propertiesPropertySource);
    }
}

META-INF/spring.factories

org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.dataflow.server.processors.DataFlowSwaggerApiEnvironmentPostProcessor

image

Description of alternatives:
I guess openapi is very common for use and it is Apache Software License so free to use. No alternatives required.

Additional context:
The default values for the properties were determined from https://springdoc.org/#properties
For normal SwaggerAPI and Springdocs are enabled by default - I changed it so that if no properties are present the API descriptions are disabled by default.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status/need-triageTeam needs to triage and take a first look

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions