Skip to content

Commit

Permalink
feat: Support generating API docs
Browse files Browse the repository at this point in the history
  • Loading branch information
CH3CHO committed Feb 4, 2025
1 parent f038c5e commit d40f78b
Show file tree
Hide file tree
Showing 65 changed files with 818 additions and 86 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: "Setup Java"
uses: actions/setup-java@v3
with:
java-version: 17
java-version: 21
distribution: 'temurin'

- name: "checkout ${{ github.ref }}"
Expand All @@ -25,7 +25,7 @@ jobs:
run: mvn clean package -f ./backend/pom.xml -Dpmd.language=en

- name: Upload Higress Console Package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: higress-console
path: backend/target/*.jar
41 changes: 38 additions & 3 deletions .github/workflows/deploy-to-oss.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,33 @@ on:
jobs:
deploy-to-oss:
runs-on: ubuntu-latest
env:
OSS_ENDPOINT: ${{ vars.OSS_ENDPOINT || 'oss-cn-hongkong.aliyuncs.com' }}
OSS_REGION: ${{ vars.OSS_REGION || 'cn-hongkong' }}
OSS_BUCKET: ${{ vars.OSS_BUCKET || 'higress-website-cn-hongkong' }}
OSS_HELM_CHART_PATH: ${{ vars.OSS_HELM_CHART_PATH || '/helm-charts' }}
OSS_API_DOCS_DIR_PATH: ${{ vars.OSS_API_DOCS_DIR_PATH || '/swagger/' }}
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install Aliyun CLI
run: |
curl -O https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar -xzf aliyun-cli-linux-latest-amd64.tgz
chmod +x aliyun
./aliyun version
- name: Download Helm Charts Index
run: ./aliyun oss cp oss://higress-website-cn-hongkong/helm-charts/index.yaml ./artifact/ -f -e oss-cn-hongkong.aliyuncs.com --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region cn-hongkong
run: ./aliyun oss cp oss://$OSS_BUCKET$OSS_HELM_CHART_PATH/index.yaml ./artifact/ -f -e $OSS_ENDPOINT --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region $OSS_REGION

- id: calc-version
name: Calculate Version Number
run: |
version=$(echo ${{ github.ref_name }} | cut -c2-)
echo "Version=$version"
echo "version=$version" >> $GITHUB_OUTPUT
- name: Build Artifact
uses: stefanprodan/kube-tools@v1
with:
Expand All @@ -34,5 +44,30 @@ jobs:
helmv3 repo index --url https://higress.io/helm-charts/ --merge ./artifact/index.yaml ./artifact
cp ./artifact/index.yaml ./artifact/cn-index.yaml
sed -i 's/higress\.io/higress\.cn/g' ./artifact/cn-index.yaml
- name: Upload to OSS
run: ./aliyun oss cp ./artifact/ oss://higress-website-cn-hongkong/helm-charts/ -r -f -e oss-cn-hongkong.aliyuncs.com --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region cn-hongkong
- name: Upload Helm Charts to OSS
run: ./aliyun oss cp ./artifact/ oss://$OSS_BUCKET$OSS_HELM_CHART_PATH/ -r -f -e $OSS_ENDPOINT --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region $OSS_REGION

- name: "Setup Java"
uses: actions/setup-java@v3
with:
java-version: 21
distribution: 'temurin'

- name: "Setup Kind"
uses: engineerd/[email protected]
with:
name: higress
version: "v0.24.0"

- name: Build Higress Console API Docs
run: |
helm repo add higress.io https://higress.cn/helm-charts
helm install higress-console -n higress-system higress.io/higress-console \
--create-namespace --set global.local=true
mvn clean verify -f ./backend/pom.xml -Papi-docs
- name: Upload API Docs to OSS
run: |
./aliyun oss cp ./backend/console/target/openapi.json oss://$OSS_BUCKET$OSS_API_DOCS_DIR_PATH -r -f -e $OSS_ENDPOINT --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region $OSS_REGION
./aliyun oss cp ./backend/console/src/main/html/swagger/ oss://$OSS_BUCKET$OSS_API_DOCS_DIR_PATH -r -f -e $OSS_ENDPOINT --access-key-id ${{ secrets.ACCESS_KEYID }} --access-key-secret ${{ secrets.ACCESS_KEYSECRET }} --region $OSS_REGION
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM openjdk:18-jdk-slim
FROM openjdk:21-jdk-slim

RUN apt-get update && apt-get install -y \
curl \
Expand Down
39 changes: 39 additions & 0 deletions backend/console/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<properties>
<nodejs.version>16.19.0</nodejs.version>
<springdoc-openapi-maven-plugin.version>1.4</springdoc-openapi-maven-plugin.version>

<app.build.version>2.1.0</app.build.version>
<app.build.dev>true</app.build.dev>
Expand Down Expand Up @@ -198,4 +199,42 @@
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>api-docs</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>start</goal>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-maven-plugin</artifactId>
<version>${springdoc-openapi-maven-plugin.version}</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<apiDocsUrl>http://localhost:8080/v3/api-docs/Higress</apiDocsUrl>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
23 changes: 23 additions & 0 deletions backend/console/src/main/html/swagger/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Higress Console API Swagger UI" />
<title>Higress Console API Swagger UI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" crossorigin></script>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: '/swagger/openapi.json',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,68 @@
*/
package com.alibaba.higress.console.config;

import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.higress.console.constant.CapabilityKey;
import com.alibaba.higress.sdk.model.ai.AiRouteFallbackStrategy;
import com.alibaba.higress.sdk.model.ai.LlmProviderProtocol;
import com.alibaba.higress.sdk.model.ai.LlmProviderType;
import com.alibaba.higress.sdk.model.consumer.CredentialType;
import com.alibaba.higress.sdk.model.consumer.KeyAuthCredentialSource;
import com.alibaba.higress.sdk.model.route.RoutePredicateTypeEnum;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Schema;

@Configuration
public class SwaggerConfig {

@Bean
public GroupedOpenApi openApi() {
return GroupedOpenApi.builder().group("Higress").displayName("Higress Console")
.addOpenApiCustomiser(this::openApiCustomizer).packagesToScan("com.alibaba.higress.console.controller")
.build();
}

@SuppressWarnings("rawtypes")
private void openApiCustomizer(OpenAPI openApi) {
Info apiInfo = new Info().title("Higress Console")
.contact(new Contact().name("CH3CHO").url("https://github.com/higress-group/higress-console")
.email("[email protected]"))
.contact(new Contact().name("Higress Authors").url("https://github.com/higress-group/higress-console"))
.description(
"Higress is a next-generation cloud-native gateway based on Alibaba's internal gateway practices.")
.license(new License().name("Apache 2.0").url("http://www.apache.org/licenses/LICENSE-2.0"));
return GroupedOpenApi.builder().group("Higress").displayName("Higress Console")
.addOpenApiCustomiser(openApi -> openApi.info(apiInfo))
.packagesToScan("com.alibaba.higress.console.controller").build();
openApi.info(apiInfo);

registerClassSchema(openApi, CapabilityKey.class);
registerClassSchema(openApi, RoutePredicateTypeEnum.class);
registerClassSchema(openApi, LlmProviderType.class);
registerClassSchema(openApi, LlmProviderProtocol.class);
registerClassSchema(openApi, AiRouteFallbackStrategy.class);
registerClassSchema(openApi, CredentialType.class);
registerClassSchema(openApi, KeyAuthCredentialSource.class);

Map<String, Schema> schemas = openApi.getComponents().getSchemas();
openApi.getComponents().setSchemas(new TreeMap<>(schemas));
}

private void registerClassSchema(OpenAPI openApi, Class<?> clazz) {
ResolvedSchema resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(clazz));
Schema<?> schema = resolvedSchema.schema;
if (StringUtils.isEmpty(schema.getName())) {
schema.setName(clazz.getSimpleName());
}
openApi.schema(schema.getName(), schema);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
*/
package com.alibaba.higress.console.constant;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Schema(description = "System Capability Keys", type = "string", allowableValues = {"CONFIG_INGRESS_V1"})
public class CapabilityKey {

public static final String CONFIG_INGRESS_V1 = "config.ingress.v1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@
import com.alibaba.higress.sdk.model.consumer.Consumer;
import com.alibaba.higress.sdk.service.consumer.ConsumerService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

@RestController("ConsumersController")
@RequestMapping("/v1/consumers")
@Validated
@Tag(name = "Consumer APIs")
public class ConsumersController {

private ConsumerService consumerService;
Expand All @@ -49,25 +55,40 @@ public void setConsumerService(ConsumerService consumerService) {
}

@GetMapping
@Operation(summary = "List consumers")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Consumers listed successfully"),
@ApiResponse(responseCode = "500", description = "Internal server error")})
public ResponseEntity<PaginatedResponse<Consumer>> list(CommonPageQuery query) {
PaginatedResult<Consumer> consumers = consumerService.list(query);
return ControllerUtil.buildResponseEntity(consumers);
}

@PostMapping
@Operation(summary = "Add a consumer")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Consumer added successfully"),
@ApiResponse(responseCode = "400", description = "Consumer data is not valid"),
@ApiResponse(responseCode = "500", description = "Internal server error")})
public ResponseEntity<Response<Consumer>> add(@RequestBody Consumer consumer) {
consumer.validate(false);
Consumer newConsumer = consumerService.addOrUpdate(consumer);
return ControllerUtil.buildResponseEntity(newConsumer);
}

@GetMapping(value = "/{name}")
@Operation(summary = "Get consumer by name")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Consumer found"),
@ApiResponse(responseCode = "404", description = "Consumer not found"),
@ApiResponse(responseCode = "500", description = "Internal server error")})
public ResponseEntity<Response<Consumer>> query(@PathVariable("name") @NotBlank String name) {
Consumer consumer = consumerService.query(name);
return ControllerUtil.buildResponseEntity(consumer);
}

@PutMapping("/{name}")
@Operation(summary = "Update an existed consumer")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Consumer updated successfully"),
@ApiResponse(responseCode = "400", description = "Consumer data is not valid"),
@ApiResponse(responseCode = "500", description = "Internal server error")})
public ResponseEntity<Response<Consumer>> put(@PathVariable("name") @NotBlank String name,
@RequestBody Consumer consumer) {
if (StringUtils.isNotEmpty(consumer.getName())) {
Expand All @@ -81,6 +102,9 @@ public ResponseEntity<Response<Consumer>> put(@PathVariable("name") @NotBlank St
}

@DeleteMapping("/{name}")
@Operation(summary = "Delete a consumer")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Consumer deleted successfully"),
@ApiResponse(responseCode = "500", description = "Internal server error")})
public ResponseEntity<Response<Consumer>> delete(@PathVariable("name") @NotBlank String name) {
consumerService.delete(name);
return ResponseEntity.noContent().build();
Expand Down
Loading

0 comments on commit d40f78b

Please sign in to comment.