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

feature: springdoc integration #4836

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<wavefront-spring-boot-bom.version>2.2.0</wavefront-spring-boot-bom.version>
<spring-cloud-dataflow-apps-docs-plugin.version>1.0.4</spring-cloud-dataflow-apps-docs-plugin.version>
<spring-cloud-dataflow-apps-metadata-plugin.version>1.0.4</spring-cloud-dataflow-apps-metadata-plugin.version>
<springdoc-openapi-ui.version>1.6.6</springdoc-openapi-ui.version>
</properties>
<modules>
<module>spring-cloud-dataflow-container-registry</module>
Expand Down Expand Up @@ -245,6 +246,11 @@
<artifactId>aws-java-sdk-ecr</artifactId>
<version>${aws-java-sdk-ecr.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc-openapi-ui.version}</version>
</dependency>
<!-- only used for dataflow managed stream applications, e.g., tasklauncher -->
<dependency>
<groupId>com.wavefront</groupId>
Expand Down
4 changes: 4 additions & 0 deletions spring-cloud-dataflow-server-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2016-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.dataflow.server.config;

import java.util.Optional;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;

public class DataFlowSwaggerApiEnvironmentPostProcessor implements EnvironmentPostProcessor {

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

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()) {
LOG.debug("Disable springdoc {} feature, because it is not set.", SPRINGDOC_API_DOCS_ENABLED_KEY);
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())) {
LOG.debug("Disable springdoc {} feature, because it is not set.", SWAGGER_UI_ENABLED_KEY);
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
*/
package org.springframework.cloud.dataflow.server.config;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.common.security.OAuthSecurityConfiguration;
import org.springframework.cloud.common.security.support.OnOAuth2SecurityEnabled;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;

/**
* Setup Spring Security OAuth for the Rest Endpoints of Spring Cloud Data Flow.
Expand All @@ -32,9 +36,42 @@
@Conditional(OnOAuth2SecurityEnabled.class)
public class DataflowOAuthSecurityConfiguration extends OAuthSecurityConfiguration {

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 + "/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2016-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.dataflow.server.support;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.util.ContentCachingResponseWrapper;

@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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.dataflow.server.config.DefaultEnvironmentPostProcessor,\
org.springframework.cloud.dataflow.server.config.MetricsReplicationEnvironmentPostProcessor
org.springframework.cloud.dataflow.server.config.MetricsReplicationEnvironmentPostProcessor,\
org.springframework.cloud.dataflow.server.config.DataFlowSwaggerApiEnvironmentPostProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.dataflow.server.config.DataFlowServerAutoConfiguration,\
org.springframework.cloud.dataflow.server.config.DataFlowControllerAutoConfiguration