fix:fix swagger not working bug. (#1225)

pull/1258/head
Haotian Zhang 10 months ago committed by GitHub
parent dd56d3cbf1
commit 38792c12a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -21,3 +21,4 @@
- [feat: support nacos namespace mapping](https://github.com/Tencent/spring-cloud-tencent/pull/1192) - [feat: support nacos namespace mapping](https://github.com/Tencent/spring-cloud-tencent/pull/1192)
- [fix:fix rule-based router when using RestTemplate.](https://github.com/Tencent/spring-cloud-tencent/pull/1201) - [fix:fix rule-based router when using RestTemplate.](https://github.com/Tencent/spring-cloud-tencent/pull/1201)
- [fix:fix sct-all wrong spring boot version obtain.](https://github.com/Tencent/spring-cloud-tencent/pull/1205) - [fix:fix sct-all wrong spring boot version obtain.](https://github.com/Tencent/spring-cloud-tencent/pull/1205)
- [fix:fix swagger not working bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1125)

@ -138,6 +138,11 @@ public class MockedConfigKVFile implements ConfigKVFile {
return false; return false;
} }
@Override
public String getMd5() {
return null;
}
@Override @Override
public void addChangeListener(ConfigFileChangeListener configFileChangeListener) { public void addChangeListener(ConfigFileChangeListener configFileChangeListener) {

@ -20,6 +20,7 @@ package com.tencent.cloud.polaris.config.condition;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.Objects;
import com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration; import com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration;
import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration; import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
@ -67,8 +68,10 @@ public class ConditionalOnReflectRefreshTypeTest {
@AfterAll @AfterAll
static void afterAll() throws IOException { static void afterAll() throws IOException {
if (Objects.nonNull(serverSocket)) {
serverSocket.close(); serverSocket.close();
} }
}
@Test @Test
public void testReflectEnabled() { public void testReflectEnabled() {

@ -22,6 +22,7 @@ import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.Collection; import java.util.Collection;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration; import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
@ -71,8 +72,10 @@ public class SpringValueProcessorTest {
@AfterAll @AfterAll
static void afterAll() throws IOException { static void afterAll() throws IOException {
if (Objects.nonNull(serverSocket)) {
serverSocket.close(); serverSocket.close();
} }
}
@Test @Test
public void springValueFiledProcessorTest() { public void springValueFiledProcessorTest() {

@ -36,28 +36,13 @@
<!-- Spring cloud dependencies start --> <!-- Spring cloud dependencies start -->
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>org.springdoc</groupId>
<artifactId>springfox-boot-starter</artifactId> <artifactId>springdoc-openapi-ui</artifactId>
<exclusions>
<exclusion>
<artifactId>swagger-models</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
<exclusion>
<artifactId>swagger-annotations</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.swagger</groupId> <groupId>org.springdoc</groupId>
<artifactId>swagger-models</artifactId> <artifactId>springdoc-openapi-webflux-ui</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency> </dependency>
<dependency> <dependency>

@ -29,15 +29,16 @@ import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.plugin.server.InterfaceDescriptor; import com.tencent.polaris.api.plugin.server.InterfaceDescriptor;
import com.tencent.polaris.api.plugin.server.ReportServiceContractRequest; import com.tencent.polaris.api.plugin.server.ReportServiceContractRequest;
import com.tencent.polaris.api.plugin.server.ReportServiceContractResponse; import com.tencent.polaris.api.plugin.server.ReportServiceContractResponse;
import io.swagger.models.HttpMethod; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.models.Operation; import io.swagger.v3.oas.models.Operation;
import io.swagger.models.Path; import io.swagger.v3.oas.models.PathItem;
import io.swagger.models.Swagger; import io.swagger.v3.oas.models.Paths;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import springfox.documentation.service.Documentation; import org.springdoc.api.AbstractOpenApiResource;
import springfox.documentation.spring.web.DocumentationCache; import org.springdoc.api.AbstractOpenApiResourceUtil;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; import org.springdoc.webflux.api.OpenApiWebFluxUtil;
import org.springdoc.webmvc.api.OpenApiWebMvcUtil;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
@ -52,18 +53,21 @@ import org.springframework.util.CollectionUtils;
public class PolarisContractReporter implements ApplicationListener<ApplicationReadyEvent> { public class PolarisContractReporter implements ApplicationListener<ApplicationReadyEvent> {
private final Logger LOG = LoggerFactory.getLogger(PolarisContractReporter.class); private final Logger LOG = LoggerFactory.getLogger(PolarisContractReporter.class);
private final ServiceModelToSwagger2Mapper swagger2Mapper;
private final DocumentationCache documentationCache; private final org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource;
private final org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource;
private final PolarisContractProperties polarisContractProperties; private final PolarisContractProperties polarisContractProperties;
private final ProviderAPI providerAPI; private final ProviderAPI providerAPI;
private final PolarisDiscoveryProperties polarisDiscoveryProperties; private final PolarisDiscoveryProperties polarisDiscoveryProperties;
public PolarisContractReporter(DocumentationCache documentationCache, ServiceModelToSwagger2Mapper swagger2Mapper, public PolarisContractReporter(org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource,
PolarisContractProperties polarisContractProperties, ProviderAPI providerAPI, PolarisDiscoveryProperties polarisDiscoveryProperties) { org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource,
this.swagger2Mapper = swagger2Mapper; PolarisContractProperties polarisContractProperties, ProviderAPI providerAPI,
this.documentationCache = documentationCache; PolarisDiscoveryProperties polarisDiscoveryProperties) {
this.multipleOpenApiWebMvcResource = multipleOpenApiWebMvcResource;
this.multipleOpenApiWebFluxResource = multipleOpenApiWebFluxResource;
this.polarisContractProperties = polarisContractProperties; this.polarisContractProperties = polarisContractProperties;
this.providerAPI = providerAPI; this.providerAPI = providerAPI;
this.polarisDiscoveryProperties = polarisDiscoveryProperties; this.polarisDiscoveryProperties = polarisDiscoveryProperties;
@ -73,29 +77,37 @@ public class PolarisContractReporter implements ApplicationListener<ApplicationR
public void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent) { public void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent) {
if (polarisContractProperties.isReportEnabled()) { if (polarisContractProperties.isReportEnabled()) {
try { try {
Documentation documentation = documentationCache.documentationByGroup(polarisContractProperties.getGroup()); AbstractOpenApiResource openApiResource = null;
Swagger swagger = swagger2Mapper.mapDocumentation(documentation); if (multipleOpenApiWebMvcResource != null) {
if (swagger != null) { openApiResource = OpenApiWebMvcUtil.getOpenApiResourceOrThrow(multipleOpenApiWebMvcResource, polarisContractProperties.getGroup());
}
else if (multipleOpenApiWebFluxResource != null) {
openApiResource = OpenApiWebFluxUtil.getOpenApiResourceOrThrow(multipleOpenApiWebFluxResource, polarisContractProperties.getGroup());
}
OpenAPI openAPI = null;
if (openApiResource != null) {
openAPI = AbstractOpenApiResourceUtil.getOpenApi(openApiResource);
}
if (openAPI != null) {
ReportServiceContractRequest request = new ReportServiceContractRequest(); ReportServiceContractRequest request = new ReportServiceContractRequest();
request.setName(polarisDiscoveryProperties.getService()); request.setName(polarisDiscoveryProperties.getService());
request.setNamespace(polarisDiscoveryProperties.getNamespace()); request.setNamespace(polarisDiscoveryProperties.getNamespace());
request.setService(polarisDiscoveryProperties.getService()); request.setService(polarisDiscoveryProperties.getService());
request.setProtocol("http"); request.setProtocol("http");
request.setVersion(polarisDiscoveryProperties.getVersion()); request.setVersion(polarisDiscoveryProperties.getVersion());
List<InterfaceDescriptor> interfaceDescriptorList = getInterfaceDescriptorFromSwagger(swagger); List<InterfaceDescriptor> interfaceDescriptorList = getInterfaceDescriptorFromSwagger(openAPI);
request.setInterfaceDescriptors(interfaceDescriptorList); request.setInterfaceDescriptors(interfaceDescriptorList);
ReportServiceContractResponse response = providerAPI.reportServiceContract(request); ReportServiceContractResponse response = providerAPI.reportServiceContract(request);
LOG.info("Service contract [Namespace: {}. Name: {}. Service: {}. Protocol:{}. Version: {}. API counter: {}] is reported.", LOG.info("Service contract [Namespace: {}. Name: {}. Service: {}. Protocol:{}. Version: {}. API counter: {}] is reported.",
request.getNamespace(), request.getName(), request.getService(), request.getProtocol(), request.getNamespace(), request.getName(), request.getService(), request.getProtocol(),
request.getVersion(), request.getInterfaceDescriptors().size()); request.getVersion(), request.getInterfaceDescriptors().size());
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
String jsonValue = JacksonUtils.serialize2Json(swagger); String jsonValue = JacksonUtils.serialize2Json(openAPI);
LOG.debug("OpenApi json data: {}", jsonValue); LOG.debug("OpenApi json data: {}", jsonValue);
} }
} }
else { else {
LOG.warn("Swagger or json is null, documentationCache keys:{}, group:{}", documentationCache.all() LOG.warn("OpenAPI or json is null, group:{}", polarisContractProperties.getGroup());
.keySet(), polarisContractProperties.getGroup());
} }
} }
catch (Throwable t) { catch (Throwable t) {
@ -104,11 +116,11 @@ public class PolarisContractReporter implements ApplicationListener<ApplicationR
} }
} }
private List<InterfaceDescriptor> getInterfaceDescriptorFromSwagger(Swagger swagger) { private List<InterfaceDescriptor> getInterfaceDescriptorFromSwagger(OpenAPI openAPI) {
List<InterfaceDescriptor> interfaceDescriptorList = new ArrayList<>(); List<InterfaceDescriptor> interfaceDescriptorList = new ArrayList<>();
Map<String, Path> paths = swagger.getPaths(); Paths paths = openAPI.getPaths();
for (Map.Entry<String, Path> p : paths.entrySet()) { for (Map.Entry<String, PathItem> p : paths.entrySet()) {
Path path = p.getValue(); PathItem path = p.getValue();
Map<String, Operation> operationMap = getOperationMapFromPath(path); Map<String, Operation> operationMap = getOperationMapFromPath(path);
if (CollectionUtils.isEmpty(operationMap)) { if (CollectionUtils.isEmpty(operationMap)) {
continue; continue;
@ -124,29 +136,29 @@ public class PolarisContractReporter implements ApplicationListener<ApplicationR
return interfaceDescriptorList; return interfaceDescriptorList;
} }
private Map<String, Operation> getOperationMapFromPath(Path path) { private Map<String, Operation> getOperationMapFromPath(PathItem path) {
Map<String, Operation> operationMap = new HashMap<>(); Map<String, Operation> operationMap = new HashMap<>();
if (path.getGet() != null) { if (path.getGet() != null) {
operationMap.put(HttpMethod.GET.name(), path.getGet()); operationMap.put(PathItem.HttpMethod.GET.name(), path.getGet());
} }
if (path.getPut() != null) { if (path.getPut() != null) {
operationMap.put(HttpMethod.PUT.name(), path.getPut()); operationMap.put(PathItem.HttpMethod.PUT.name(), path.getPut());
} }
if (path.getPost() != null) { if (path.getPost() != null) {
operationMap.put(HttpMethod.POST.name(), path.getPost()); operationMap.put(PathItem.HttpMethod.POST.name(), path.getPost());
} }
if (path.getHead() != null) { if (path.getHead() != null) {
operationMap.put(HttpMethod.HEAD.name(), path.getHead()); operationMap.put(PathItem.HttpMethod.HEAD.name(), path.getHead());
} }
if (path.getDelete() != null) { if (path.getDelete() != null) {
operationMap.put(HttpMethod.DELETE.name(), path.getDelete()); operationMap.put(PathItem.HttpMethod.DELETE.name(), path.getDelete());
} }
if (path.getPatch() != null) { if (path.getPatch() != null) {
operationMap.put(HttpMethod.PATCH.name(), path.getPatch()); operationMap.put(PathItem.HttpMethod.PATCH.name(), path.getPatch());
} }
if (path.getOptions() != null) { if (path.getOptions() != null) {
operationMap.put(HttpMethod.OPTIONS.name(), path.getOptions()); operationMap.put(PathItem.HttpMethod.OPTIONS.name(), path.getOptions());
} }
return operationMap; return operationMap;

@ -17,13 +17,12 @@
package com.tencent.cloud.polaris.contract.config; package com.tencent.cloud.polaris.contract.config;
import javax.annotation.Nullable;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
/** /**
* Auto configuration for Polaris contract properties. * Auto configuration for Polaris contract properties.

@ -17,11 +17,6 @@
package com.tencent.cloud.polaris.contract.config; package com.tencent.cloud.polaris.contract.config;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager; import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
@ -30,13 +25,13 @@ import com.tencent.cloud.polaris.contract.PolarisSwaggerApplicationListener;
import com.tencent.cloud.polaris.contract.filter.ApiDocServletFilter; import com.tencent.cloud.polaris.contract.filter.ApiDocServletFilter;
import com.tencent.cloud.polaris.contract.filter.ApiDocWebFluxFilter; import com.tencent.cloud.polaris.contract.filter.ApiDocWebFluxFilter;
import com.tencent.cloud.polaris.contract.utils.PackageUtil; import com.tencent.cloud.polaris.contract.utils.PackageUtil;
import springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration; import io.swagger.v3.oas.models.OpenAPI;
import springfox.documentation.builders.ApiInfoBuilder; import io.swagger.v3.oas.models.info.Info;
import springfox.documentation.service.Contact; import io.swagger.v3.oas.models.info.License;
import springfox.documentation.spi.DocumentationType; import org.springdoc.core.GroupedOpenApi;
import springfox.documentation.spring.web.DocumentationCache; import org.springdoc.core.SpringDocConfiguration;
import springfox.documentation.spring.web.plugins.Docket; import org.springdoc.webflux.api.MultipleOpenApiWebFluxResource;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; import org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -45,6 +40,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import static com.tencent.cloud.polaris.contract.utils.PackageUtil.SPLITTER;
/** /**
* Auto configuration for Polaris swagger. * Auto configuration for Polaris swagger.
@ -54,75 +53,48 @@ import org.springframework.context.annotation.Import;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled @ConditionalOnPolarisEnabled
@ConditionalOnProperty(name = "spring.cloud.polaris.contract.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "spring.cloud.polaris.contract.enabled", havingValue = "true", matchIfMissing = true)
@Import(OpenApiAutoConfiguration.class) @Import(SpringDocConfiguration.class)
public class PolarisSwaggerAutoConfiguration { public class PolarisSwaggerAutoConfiguration {
static {
// After springboot2.6.x, the default path matching strategy of spring MVC is changed from ANT_PATH_MATCHER
// mode to PATH_PATTERN_PARSER mode, causing an error. The solution is to switch to the original ANT_PATH_MATCHER mode.
System.setProperty("spring.mvc.pathmatch.matching-strategy", "ant-path-matcher");
}
@Bean @Bean
public Docket polarisDocket(PolarisContractProperties polarisContractProperties) { public GroupedOpenApi polarisGroupedOpenApi(PolarisContractProperties polarisContractProperties) {
List<Predicate<String>> excludePathList = PackageUtil.getExcludePathPredicates(polarisContractProperties.getExcludePath());
List<Predicate<String>> basePathList = PackageUtil.getBasePathPredicates(polarisContractProperties.getBasePath());
String basePackage = PackageUtil.scanPackage(polarisContractProperties.getBasePackage()); String basePackage = PackageUtil.scanPackage(polarisContractProperties.getBasePackage());
String[] basePaths = {};
Predicate<String> basePathListOr = null; if (StringUtils.hasText(polarisContractProperties.getBasePath())) {
for (Predicate<String> basePathPredicate : basePathList) { basePaths = polarisContractProperties.getBasePath().split(SPLITTER);
if (basePathListOr == null) {
basePathListOr = basePathPredicate;
} }
else { String[] excludePaths = {};
basePathListOr = basePathListOr.or(basePathPredicate); if (StringUtils.hasText(polarisContractProperties.getExcludePath())) {
excludePaths = polarisContractProperties.getExcludePath().split(SPLITTER);
} }
return GroupedOpenApi.builder()
.packagesToScan(basePackage)
.pathsToMatch(basePaths)
.pathsToExclude(excludePaths)
.group(polarisContractProperties.getGroup())
.build();
} }
Predicate<String> excludePathListOr = null; @Bean
for (Predicate<String> excludePathPredicate : excludePathList) { public OpenAPI polarisOpenAPI() {
if (excludePathListOr == null) { return new OpenAPI()
excludePathListOr = excludePathPredicate; .info(new Info()
}
else {
excludePathListOr = excludePathListOr.or(excludePathPredicate);
}
}
Predicate<String> pathsPredicate = basePathListOr;
if (excludePathListOr != null) {
excludePathListOr = excludePathListOr.negate();
pathsPredicate = pathsPredicate.and(excludePathListOr);
}
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(PackageUtil.basePackage(basePackage))
.paths(pathsPredicate)
.build()
.groupName(polarisContractProperties.getGroup())
.enable(polarisContractProperties.isEnabled())
.directModelSubstitute(LocalDate.class, Date.class)
.apiInfo(new ApiInfoBuilder()
.title("Polaris Swagger API") .title("Polaris Swagger API")
.description("This is to show polaris api description.") .description("This is to show polaris api description.")
.license("BSD-3-Clause") .license(new License().name("BSD-3-Clause").url("https://opensource.org/licenses/BSD-3-Clause"))
.licenseUrl("https://opensource.org/licenses/BSD-3-Clause") .version("1.0.0"));
.termsOfServiceUrl("")
.version("1.0.0")
.contact(new Contact("", "", ""))
.build());
} }
@Bean @Bean
@ConditionalOnBean(Docket.class) @ConditionalOnBean(OpenAPI.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisContractReporter polarisContractReporter(DocumentationCache documentationCache, public PolarisContractReporter polarisContractReporter(
ServiceModelToSwagger2Mapper swagger2Mapper, PolarisContractProperties polarisContractProperties, @Nullable MultipleOpenApiWebMvcResource multipleOpenApiWebMvcResource,
PolarisSDKContextManager polarisSDKContextManager, PolarisDiscoveryProperties polarisDiscoveryProperties) { @Nullable MultipleOpenApiWebFluxResource multipleOpenApiWebFluxResource,
return new PolarisContractReporter(documentationCache, swagger2Mapper, polarisContractProperties, PolarisContractProperties polarisContractProperties, PolarisSDKContextManager polarisSDKContextManager,
polarisSDKContextManager.getProviderAPI(), polarisDiscoveryProperties); PolarisDiscoveryProperties polarisDiscoveryProperties) {
return new PolarisContractReporter(multipleOpenApiWebMvcResource, multipleOpenApiWebFluxResource,
polarisContractProperties, polarisSDKContextManager.getProviderAPI(), polarisDiscoveryProperties);
} }
@Bean @Bean

@ -26,7 +26,6 @@ import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties; import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.polaris.contract.filter.FilterConstant.SWAGGER_RESOURCE_PREFIX; import static com.tencent.cloud.polaris.contract.filter.FilterConstant.SWAGGER_RESOURCE_PREFIX;
@ -51,11 +50,9 @@ public class ApiDocServletFilter extends OncePerRequestFilter {
} }
@Override @Override
public void doFilterInternal(@NonNull HttpServletRequest httpServletRequest, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
@NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain)
throws ServletException, IOException {
if (!polarisContractProperties.isExposure()) { if (!polarisContractProperties.isExposure()) {
String path = httpServletRequest.getServletPath(); String path = request.getServletPath();
if (path.startsWith(SWAGGER_V2_API_DOC_URL) || if (path.startsWith(SWAGGER_V2_API_DOC_URL) ||
path.startsWith(SWAGGER_V3_API_DOC_URL) || path.startsWith(SWAGGER_V3_API_DOC_URL) ||
path.startsWith(SWAGGER_UI_V2_URL) || path.startsWith(SWAGGER_UI_V2_URL) ||
@ -63,11 +60,11 @@ public class ApiDocServletFilter extends OncePerRequestFilter {
path.startsWith(SWAGGER_RESOURCE_PREFIX) || path.startsWith(SWAGGER_RESOURCE_PREFIX) ||
path.startsWith(SWAGGER_WEBJARS_V2_PREFIX) || path.startsWith(SWAGGER_WEBJARS_V2_PREFIX) ||
path.startsWith(SWAGGER_WEBJARS_V3_PREFIX)) { path.startsWith(SWAGGER_WEBJARS_V3_PREFIX)) {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return; return;
} }
} }
filterChain.doFilter(httpServletRequest, httpServletResponse); filterChain.doFilter(request, response);
} }
} }

@ -17,13 +17,13 @@
package com.tencent.cloud.polaris.contract.filter; package com.tencent.cloud.polaris.contract.filter;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties; import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
@ -42,6 +42,7 @@ import static com.tencent.cloud.polaris.contract.filter.FilterConstant.SWAGGER_W
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class ApiDocWebFluxFilter implements WebFilter { public class ApiDocWebFluxFilter implements WebFilter {
private final PolarisContractProperties polarisContractProperties; private final PolarisContractProperties polarisContractProperties;
public ApiDocWebFluxFilter(PolarisContractProperties polarisContractProperties) { public ApiDocWebFluxFilter(PolarisContractProperties polarisContractProperties) {
@ -49,7 +50,7 @@ public class ApiDocWebFluxFilter implements WebFilter {
} }
@Override @Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { public Mono<Void> filter(ServerWebExchange serverWebExchange, @NonNull WebFilterChain webFilterChain) {
if (!polarisContractProperties.isExposure()) { if (!polarisContractProperties.isExposure()) {
String path = serverWebExchange.getRequest().getURI().getPath(); String path = serverWebExchange.getRequest().getURI().getPath();
if (path.startsWith(SWAGGER_V2_API_DOC_URL) || if (path.startsWith(SWAGGER_V2_API_DOC_URL) ||

@ -17,26 +17,17 @@
package com.tencent.cloud.polaris.contract.utils; package com.tencent.cloud.polaris.contract.utils;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.tencent.cloud.polaris.contract.SwaggerContext; import com.tencent.cloud.polaris.contract.SwaggerContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static com.google.common.base.Optional.fromNullable;
/** /**
* Util for package processing. * Util for package processing.
* *
@ -44,87 +35,15 @@ import static com.google.common.base.Optional.fromNullable;
*/ */
public final class PackageUtil { public final class PackageUtil {
/**
* splitter for property.
*/
public static final String SPLITTER = ",";
private static final Logger LOG = LoggerFactory.getLogger(PackageUtil.class); private static final Logger LOG = LoggerFactory.getLogger(PackageUtil.class);
private static final String SPLITTER = ",";
private PackageUtil() { private PackageUtil() {
} }
public static Predicate<RequestHandler> basePackage(String basePackage) {
return input -> declaringClass(input).transform(handlerPackage(basePackage, SPLITTER)).or(false);
}
public static Optional<Class<?>> declaringClass(RequestHandler input) {
if (input == null) {
return Optional.absent();
}
return fromNullable(input.declaringClass());
}
public static Function<Class<?>, Boolean> handlerPackage(String basePackage, String splitter) {
return input -> {
try {
if (StringUtils.isEmpty(basePackage)) {
return false;
}
String[] packages = basePackage.trim().split(splitter);
// Loop to determine matching
for (String strPackage : packages) {
if (input == null) {
continue;
}
Package pkg = input.getPackage();
if (pkg == null) {
continue;
}
String name = pkg.getName();
if (StringUtils.isEmpty(name)) {
continue;
}
boolean isMatch = name.startsWith(strPackage);
if (isMatch) {
return true;
}
}
}
catch (Exception e) {
LOG.error("handler package error", e);
}
return false;
};
}
public static List<Predicate<String>> getExcludePathPredicates(String excludePath) {
List<Predicate<String>> excludePathList = new ArrayList<>();
if (StringUtils.isEmpty(excludePath)) {
return excludePathList;
}
String[] exs = excludePath.split(SPLITTER);
for (String ex : exs) {
if (!StringUtils.isEmpty(ex)) {
excludePathList.add(PathSelectors.ant(ex));
}
}
return excludePathList;
}
public static List<Predicate<String>> getBasePathPredicates(String basePath) {
List<Predicate<String>> basePathList = new ArrayList<>();
if (!StringUtils.isEmpty(basePath)) {
String[] bps = basePath.split(SPLITTER);
for (String bp : bps) {
if (!StringUtils.isEmpty(bp)) {
basePathList.add(PathSelectors.ant(bp));
}
}
}
if (basePathList.isEmpty()) {
basePathList.add(PathSelectors.ant("/**"));
}
return basePathList;
}
public static String scanPackage(String configBasePackage) { public static String scanPackage(String configBasePackage) {
String validScanPackage; String validScanPackage;
// Externally configured scan package // Externally configured scan package

@ -0,0 +1,37 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.springdoc.api;
import java.util.Locale;
import io.swagger.v3.oas.models.OpenAPI;
/**
* Util for {@link AbstractOpenApiResource}.
*
* @author Haotian Zhang
*/
public final class AbstractOpenApiResourceUtil {
private AbstractOpenApiResourceUtil() {
}
public static OpenAPI getOpenApi(AbstractOpenApiResource openApiResource) {
return openApiResource.getOpenApi(Locale.getDefault());
}
}

@ -0,0 +1,36 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.springdoc.webflux.api;
import org.springdoc.api.AbstractOpenApiResource;
/**
* Util for {@link MultipleOpenApiResource}.
*
* @author Haotian Zhang
*/
public final class OpenApiWebFluxUtil {
private OpenApiWebFluxUtil() {
}
public static AbstractOpenApiResource getOpenApiResourceOrThrow(
org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource, String groupName) {
return multipleOpenApiWebFluxResource.getOpenApiResourceOrThrow(groupName);
}
}

@ -0,0 +1,35 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.springdoc.webmvc.api;
import org.springdoc.api.AbstractOpenApiResource;
/**
* Util for {@link MultipleOpenApiResource}.
*
* @author Haotian Zhang
*/
public final class OpenApiWebMvcUtil {
private OpenApiWebMvcUtil() {
}
public static AbstractOpenApiResource getOpenApiResourceOrThrow(
org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource, String groupName) {
return multipleOpenApiWebMvcResource.getOpenApiResourceOrThrow(groupName);
}
}

@ -1,90 +0,0 @@
/*
*
* Copyright 2015 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
*
* http://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 springfox.documentation.spring.web;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import static springfox.documentation.spring.web.paths.Paths.maybeChompLeadingSlash;
import static springfox.documentation.spring.web.paths.Paths.maybeChompTrailingSlash;
/**
* Modified to be compatible with spring-boot-actuator.
*/
public class WebMvcPatternsRequestConditionWrapper
implements springfox.documentation.spring.wrapper.PatternsRequestCondition<PatternsRequestCondition> {
private final String contextPath;
private final PatternsRequestCondition condition;
public WebMvcPatternsRequestConditionWrapper(
String contextPath,
PatternsRequestCondition condition) {
this.contextPath = contextPath;
this.condition = condition;
}
@Override
public springfox.documentation.spring.wrapper.PatternsRequestCondition combine(
springfox.documentation.spring.wrapper.PatternsRequestCondition<PatternsRequestCondition> other) {
if (other instanceof WebMvcPatternsRequestConditionWrapper && !this.equals(other)) {
return new WebMvcPatternsRequestConditionWrapper(
contextPath,
condition.combine(((WebMvcPatternsRequestConditionWrapper) other).condition));
}
return this;
}
@Override
public Set<String> getPatterns() {
// polaris add start
if (this.condition == null) {
return new HashSet<>();
}
// polaris add end
return this.condition.getPatterns().stream()
.map(p -> String.format("%s/%s", maybeChompTrailingSlash(contextPath), maybeChompLeadingSlash(p)))
.collect(Collectors.toSet());
}
@Override
public boolean equals(Object o) {
if (o instanceof WebMvcPatternsRequestConditionWrapper) {
return this.condition.equals(((WebMvcPatternsRequestConditionWrapper) o).condition);
}
return false;
}
@Override
public int hashCode() {
return this.condition.hashCode();
}
@Override
public String toString() {
return this.condition.toString();
}
}

@ -1,175 +0,0 @@
/*
*
* Copyright 2016-2017 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
*
* http://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 springfox.documentation.spring.web;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import com.fasterxml.classmate.ResolvedType;
import springfox.documentation.RequestHandler;
import springfox.documentation.RequestHandlerKey;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spring.web.readers.operation.HandlerMethodResolver;
import springfox.documentation.spring.wrapper.NameValueExpression;
import springfox.documentation.spring.wrapper.PatternsRequestCondition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import static java.util.Optional.ofNullable;
/**
* Modified to be compatible with spring-boot-actuator.
*/
public class WebMvcRequestHandler implements RequestHandler {
private final String contextPath;
private final HandlerMethodResolver methodResolver;
private final RequestMappingInfo requestMapping;
private final HandlerMethod handlerMethod;
public WebMvcRequestHandler(
String contextPath,
HandlerMethodResolver methodResolver,
RequestMappingInfo requestMapping,
HandlerMethod handlerMethod) {
this.contextPath = contextPath;
this.methodResolver = methodResolver;
this.requestMapping = requestMapping;
this.handlerMethod = handlerMethod;
}
@Override
public HandlerMethod getHandlerMethod() {
return handlerMethod;
}
@Override
public RequestHandler combine(RequestHandler other) {
return this;
}
@Override
public Class<?> declaringClass() {
return handlerMethod.getBeanType();
}
@Override
public boolean isAnnotatedWith(Class<? extends Annotation> annotation) {
return null != AnnotationUtils.findAnnotation(handlerMethod.getMethod(), annotation);
}
@Override
public PatternsRequestCondition getPatternsCondition() {
return new WebMvcPatternsRequestConditionWrapper(
contextPath,
requestMapping.getPatternsCondition());
}
@Override
public String groupName() {
return ControllerNamingUtils.controllerNameAsGroup(handlerMethod);
}
@Override
public String getName() {
return handlerMethod.getMethod().getName();
}
@Override
public Set<RequestMethod> supportedMethods() {
return requestMapping.getMethodsCondition().getMethods();
}
@Override
public Set<MediaType> produces() {
return requestMapping.getProducesCondition().getProducibleMediaTypes();
}
@Override
public Set<MediaType> consumes() {
return requestMapping.getConsumesCondition().getConsumableMediaTypes();
}
@Override
public Set<NameValueExpression<String>> headers() {
return WebMvcNameValueExpressionWrapper.from(requestMapping.getHeadersCondition().getExpressions());
}
@Override
public Set<NameValueExpression<String>> params() {
return WebMvcNameValueExpressionWrapper.from(requestMapping.getParamsCondition().getExpressions());
}
@Override
public <T extends Annotation> Optional<T> findAnnotation(Class<T> annotation) {
return ofNullable(AnnotationUtils.findAnnotation(handlerMethod.getMethod(), annotation));
}
@Override
public RequestHandlerKey key() {
// polaris add start
Set<String> patterns = new HashSet<>();
if (requestMapping.getPatternsCondition() != null && requestMapping.getPatternsCondition()
.getPatterns() != null) {
patterns = requestMapping.getPatternsCondition().getPatterns();
}
// polaris add end
return new RequestHandlerKey(
patterns,
requestMapping.getMethodsCondition().getMethods(),
requestMapping.getConsumesCondition().getConsumableMediaTypes(),
requestMapping.getProducesCondition().getProducibleMediaTypes());
}
@Override
public springfox.documentation.spring.wrapper.RequestMappingInfo<?> getRequestMapping() {
return new WebMvcRequestMappingInfoWrapper(requestMapping);
}
@Override
public List<ResolvedMethodParameter> getParameters() {
return methodResolver.methodParameters(handlerMethod);
}
@Override
public ResolvedType getReturnType() {
return methodResolver.methodReturnType(handlerMethod);
}
@Override
public <T extends Annotation> Optional<T> findControllerAnnotation(Class<T> annotation) {
return ofNullable(AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), annotation));
}
@Override
public String toString() {
return new StringJoiner(", ", WebMvcRequestHandler.class.getSimpleName() + "{", "}")
.add("requestMapping=" + requestMapping)
.add("handlerMethod=" + handlerMethod)
.add("key=" + key())
.toString();
}
}

@ -76,7 +76,7 @@
<polaris.version>1.15.0-SNAPSHOT</polaris.version> <polaris.version>1.15.0-SNAPSHOT</polaris.version>
<guava.version>32.0.1-jre</guava.version> <guava.version>32.0.1-jre</guava.version>
<logback.version>1.2.13</logback.version> <logback.version>1.2.13</logback.version>
<springfox.swagger2.version>3.0.0</springfox.swagger2.version> <springdoc.version>1.7.0</springdoc.version>
<io.swagger.version>1.5.24</io.swagger.version> <io.swagger.version>1.5.24</io.swagger.version>
<mocktio.version>4.5.1</mocktio.version> <mocktio.version>4.5.1</mocktio.version>
<byte-buddy.version>1.12.10</byte-buddy.version> <byte-buddy.version>1.12.10</byte-buddy.version>
@ -239,21 +239,15 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>org.springdoc</groupId>
<artifactId>springfox-boot-starter</artifactId> <artifactId>springdoc-openapi-ui</artifactId>
<version>${springfox.swagger2.version}</version> <version>${springdoc.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.swagger</groupId> <groupId>org.springdoc</groupId>
<artifactId>swagger-models</artifactId> <artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>${io.swagger.version}</version> <version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${io.swagger.version}</version>
</dependency> </dependency>
<dependency> <dependency>

@ -14,7 +14,7 @@ spring:
contract: contract:
exposure: true exposure: true
report: report:
enabled: false enabled: true
stat: stat:
enabled: true enabled: true
port: 28083 port: 28083

@ -14,7 +14,7 @@ spring:
contract: contract:
exposure: true exposure: true
report: report:
enabled: false enabled: true
stat: stat:
enabled: true enabled: true
port: 28084 port: 28084

@ -17,7 +17,7 @@ spring:
contract: contract:
exposure: true exposure: true
report: report:
enabled: false enabled: true
circuitbreaker: circuitbreaker:
enabled: true enabled: true
stat: stat:

@ -21,7 +21,7 @@ spring:
contract: contract:
exposure: true exposure: true
report: report:
enabled: false enabled: true
stat: stat:
enabled: true enabled: true
port: 28081 port: 28081

Loading…
Cancel
Save