Skip to content

Commit

Permalink
feat:support service contract reporting. (#1141)
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyeBeFreeman authored Sep 26, 2023
1 parent e40ea73 commit e4e5f5a
Show file tree
Hide file tree
Showing 56 changed files with 1,332 additions and 1,907 deletions.
2 changes: 1 addition & 1 deletion .licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ header:
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
paths:
- "**/java/**"
- "**/tencent/**"
language:
Java:
extensions:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
- [Refactoring:remove invalid @AutoConfigureAfter and @AutoConfigureBefore from discovery client automatic configuration.](https://github.com/Tencent/spring-cloud-tencent/pull/1116)
- [fix:fix feign url bug when using sleuth.](https://github.com/Tencent/spring-cloud-tencent/pull/1120)
- [refactor:optimize the order and condition matching of service registration automatic configuration.](https://github.com/Tencent/spring-cloud-tencent/pull/1131)
- [feat:support service contract reporting.](https://github.com/Tencent/spring-cloud-tencent/pull/1141)
- [feat: support log path configuration parameters,](https://github.com/Tencent/spring-cloud-tencent/pull/1142)
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<module>spring-cloud-starter-tencent-polaris-ratelimit</module>
<module>spring-cloud-starter-tencent-polaris-circuitbreaker</module>
<module>spring-cloud-starter-tencent-polaris-router</module>
<module>spring-cloud-starter-tencent-polaris-contract</module>
<module>spring-cloud-tencent-plugin-starters</module>
<module>spring-cloud-tencent-dependencies</module>
<module>spring-cloud-starter-tencent-all</module>
Expand Down
22 changes: 17 additions & 5 deletions spring-cloud-starter-tencent-all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>

<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-contract</artifactId>
</dependency>

<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
Expand Down Expand Up @@ -88,12 +93,19 @@
<configuration>
<createSourcesJar>true</createSourcesJar>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports</resource>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports
</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</resource>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
</resource>
</transformer>
</transformers>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ public PropertySource<?> locate(Environment environment) {

private void initCustomPolarisConfigExtensionFiles(CompositePropertySource compositePropertySource) {
if (polarisConfigCustomExtensionLayer == null) {
LOGGER.debug("[SCT Config] PolarisAdaptorTsfConfigExtensionLayer is not init, ignore the following execution steps");
LOGGER.debug("[SCT Config] PolarisConfigCustomExtensionLayer is not init, ignore the following execution steps");
return;
}
polarisConfigCustomExtensionLayer.initConfigFiles(environment, compositePropertySource, polarisPropertySourceManager, configFileService);
}

private void afterLocatePolarisConfigExtension(CompositePropertySource compositePropertySource) {
if (polarisConfigCustomExtensionLayer == null) {
LOGGER.debug("[SCT Config] PolarisAdaptorTsfConfigExtensionLayer is not init, ignore the following execution steps");
LOGGER.debug("[SCT Config] PolarisConfigCustomExtensionLayer is not init, ignore the following execution steps");
return;
}
polarisConfigCustomExtensionLayer.executeAfterLocateConfigReturning(compositePropertySource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private void registerPolarisConfigPublishEvent() {

private void customInitRegisterPolarisConfig(PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher) {
if (polarisConfigCustomExtensionLayer == null) {
LOGGER.debug("[SCT Config] PolarisAdaptorTsfConfigExtensionLayer is not init, ignore the following execution steps");
LOGGER.debug("[SCT Config] PolarisConfigCustomExtensionLayer is not init, ignore the following execution steps");
return;
}
polarisConfigCustomExtensionLayer.initRegisterConfig(polarisConfigPropertyAutoRefresher);
Expand Down Expand Up @@ -139,7 +139,7 @@ public void registerPolarisConfigPublishChangeListener(PolarisPropertySource pol

private void customRegisterPolarisConfigPublishChangeListener(PolarisPropertySource polarisPropertySource) {
if (polarisConfigCustomExtensionLayer == null) {
LOGGER.debug("[SCT Config] PolarisAdaptorTsfConfigExtensionLayer is not init, ignore the following execution steps");
LOGGER.debug("[SCT Config] PolarisConfigCustomExtensionLayer is not init, ignore the following execution steps");
return;
}
polarisConfigCustomExtensionLayer.executeRegisterPublishChangeListener(polarisPropertySource);
Expand Down
54 changes: 54 additions & 0 deletions spring-cloud-starter-tencent-polaris-contract/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-starter-tencent-polaris-contract</artifactId>
<name>Spring Cloud Starter Tencent Polaris Contract</name>

<dependencies>
<!-- Spring Cloud Tencent dependencies start -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->

<!-- Spring cloud dependencies start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring cloud dependencies start -->

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* 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 com.tencent.cloud.polaris.contract;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.plugin.server.InterfaceDescriptor;
import com.tencent.polaris.api.plugin.server.ReportServiceContractRequest;
import com.tencent.polaris.api.plugin.server.ReportServiceContractResponse;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.api.AbstractOpenApiResource;
import org.springdoc.api.AbstractOpenApiResourceUtil;
import org.springdoc.webflux.api.OpenApiWebFluxUtil;
import org.springdoc.webmvc.api.OpenApiWebMvcUtil;

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;

/**
* Polaris contract reporter.
*
* @author Haotian Zhang
*/
public class PolarisContractReporter implements ApplicationListener<ApplicationReadyEvent> {

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

private final org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource;
private final org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource;
private final String groupName;

private final ProviderAPI providerAPI;

private final PolarisDiscoveryProperties polarisDiscoveryProperties;

public PolarisContractReporter(org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource,
org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource,
String groupName, ProviderAPI providerAPI,
PolarisDiscoveryProperties polarisDiscoveryProperties) {
this.multipleOpenApiWebMvcResource = multipleOpenApiWebMvcResource;
this.multipleOpenApiWebFluxResource = multipleOpenApiWebFluxResource;
this.groupName = groupName;
this.providerAPI = providerAPI;
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
}

@Override
public void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent) {
try {
AbstractOpenApiResource openApiResource = null;
if (multipleOpenApiWebMvcResource != null) {
openApiResource = OpenApiWebMvcUtil.getOpenApiResourceOrThrow(multipleOpenApiWebMvcResource, groupName);
}
else if (multipleOpenApiWebFluxResource != null) {
openApiResource = OpenApiWebFluxUtil.getOpenApiResourceOrThrow(multipleOpenApiWebFluxResource, groupName);
}
OpenAPI openAPI = null;
if (openApiResource != null) {
openAPI = AbstractOpenApiResourceUtil.getOpenApi(openApiResource);
}
if (openAPI != null) {
ReportServiceContractRequest request = new ReportServiceContractRequest();
request.setName(polarisDiscoveryProperties.getService());
request.setNamespace(polarisDiscoveryProperties.getNamespace());
request.setService(polarisDiscoveryProperties.getService());
request.setProtocol("http");
request.setVersion(polarisDiscoveryProperties.getVersion());
List<InterfaceDescriptor> interfaceDescriptorList = getInterfaceDescriptorFromSwagger(openAPI);
request.setInterfaceDescriptors(interfaceDescriptorList);
ReportServiceContractResponse response = providerAPI.reportServiceContract(request);
LOG.info("Service contract [Namespace: {}. Name: {}. Service: {}. Protocol:{}. Version: {}. API counter: {}] is reported.",
request.getNamespace(), request.getName(), request.getService(), request.getProtocol(),
request.getVersion(), request.getInterfaceDescriptors().size());
if (LOG.isDebugEnabled()) {
String jsonValue = JacksonUtils.serialize2Json(openAPI);
LOG.debug("OpenApi json data: {}", jsonValue);
}
}
else {
LOG.warn("OpenAPI or json is null, group:{}", groupName);
}
}
catch (Throwable t) {
LOG.error("Report contract failed.", t);
}
}

private List<InterfaceDescriptor> getInterfaceDescriptorFromSwagger(OpenAPI openAPI) {
List<InterfaceDescriptor> interfaceDescriptorList = new ArrayList<>();
Paths paths = openAPI.getPaths();
for (Map.Entry<String, PathItem> p : paths.entrySet()) {
PathItem path = p.getValue();
Map<String, Operation> operationMap = getOperationMapFromPath(path);
if (CollectionUtils.isEmpty(operationMap)) {
continue;
}
for (Map.Entry<String, Operation> o : operationMap.entrySet()) {
InterfaceDescriptor interfaceDescriptor = new InterfaceDescriptor();
interfaceDescriptor.setPath(p.getKey());
interfaceDescriptor.setMethod(o.getKey());
interfaceDescriptor.setContent(JacksonUtils.serialize2Json(p.getValue()));
interfaceDescriptorList.add(interfaceDescriptor);
}
}
return interfaceDescriptorList;
}

private Map<String, Operation> getOperationMapFromPath(PathItem path) {
Map<String, Operation> operationMap = new HashMap<>();

if (path.getGet() != null) {
operationMap.put(PathItem.HttpMethod.GET.name(), path.getGet());
}
if (path.getPut() != null) {
operationMap.put(PathItem.HttpMethod.PUT.name(), path.getPut());
}
if (path.getPost() != null) {
operationMap.put(PathItem.HttpMethod.POST.name(), path.getPost());
}
if (path.getHead() != null) {
operationMap.put(PathItem.HttpMethod.HEAD.name(), path.getHead());
}
if (path.getDelete() != null) {
operationMap.put(PathItem.HttpMethod.DELETE.name(), path.getDelete());
}
if (path.getPatch() != null) {
operationMap.put(PathItem.HttpMethod.PATCH.name(), path.getPatch());
}
if (path.getOptions() != null) {
operationMap.put(PathItem.HttpMethod.OPTIONS.name(), path.getOptions());
}

return operationMap;
}
}
Original file line number Diff line number Diff line change
@@ -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 com.tencent.cloud.polaris.contract;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;

public class PolarisSwaggerApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent startingEvent) {
SpringApplication application = startingEvent.getSpringApplication();
Class<?> mainClass = application.getMainApplicationClass();
if (mainClass == null) {
return;
}
SwaggerContext.setAttribute(String.format("$%s", "MainClass"), mainClass);
}
}
Original file line number Diff line number Diff line change
@@ -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 com.tencent.cloud.polaris.contract;

import java.util.concurrent.ConcurrentHashMap;

public final class SwaggerContext {
private static final ConcurrentHashMap<String, Object> attribute = new ConcurrentHashMap<>();

private SwaggerContext() {

}

public static void setAttribute(String key, Object value) {
attribute.put(key, value);
}

public static Object getAttribute(String key) {
return attribute.get(key);
}
}
Loading

0 comments on commit e4e5f5a

Please sign in to comment.