|
@@ -0,0 +1,272 @@
|
|
|
+package com.ruoyi.framework.handler;
|
|
|
+
|
|
|
+import cn.hutool.core.io.IoUtil;
|
|
|
+import io.swagger.v3.core.jackson.TypeNameResolver;
|
|
|
+import io.swagger.v3.core.util.AnnotationsUtils;
|
|
|
+import io.swagger.v3.oas.annotations.tags.Tags;
|
|
|
+import io.swagger.v3.oas.models.Components;
|
|
|
+import io.swagger.v3.oas.models.OpenAPI;
|
|
|
+import io.swagger.v3.oas.models.Operation;
|
|
|
+import io.swagger.v3.oas.models.Paths;
|
|
|
+import io.swagger.v3.oas.models.tags.Tag;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springdoc.core.OpenAPIService;
|
|
|
+import org.springdoc.core.PropertyResolverUtils;
|
|
|
+import org.springdoc.core.SecurityService;
|
|
|
+import org.springdoc.core.SpringDocConfigProperties;
|
|
|
+import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
|
|
+import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
|
|
+import org.springdoc.core.providers.JavadocProvider;
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
|
+import org.springframework.core.annotation.AnnotatedElementUtils;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
+import org.springframework.web.method.HandlerMethod;
|
|
|
+
|
|
|
+import java.io.StringReader;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+import java.util.stream.Stream;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 自定义 openapi 处理器
|
|
|
+ * 对源码功能进行修改 增强使用
|
|
|
+ */
|
|
|
+@SuppressWarnings("all")
|
|
|
+public class OpenApiHandler extends OpenAPIService {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The constant LOGGER.
|
|
|
+ */
|
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Context.
|
|
|
+ */
|
|
|
+ private ApplicationContext context;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Security parser.
|
|
|
+ */
|
|
|
+ private final SecurityService securityParser;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Mappings map.
|
|
|
+ */
|
|
|
+ private final Map<String, Object> mappingsMap = new HashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Springdoc tags.
|
|
|
+ */
|
|
|
+ private final Map<HandlerMethod, io.swagger.v3.oas.models.tags.Tag> springdocTags = new HashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Open api builder customisers.
|
|
|
+ */
|
|
|
+ private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The server base URL customisers.
|
|
|
+ */
|
|
|
+ private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Spring doc config properties.
|
|
|
+ */
|
|
|
+ private final SpringDocConfigProperties springDocConfigProperties;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Open api.
|
|
|
+ */
|
|
|
+ private OpenAPI openAPI;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Cached open api map.
|
|
|
+ */
|
|
|
+ private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Is servers present.
|
|
|
+ */
|
|
|
+ private boolean isServersPresent;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Server base url.
|
|
|
+ */
|
|
|
+ private String serverBaseUrl;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Property resolver utils.
|
|
|
+ */
|
|
|
+ private final PropertyResolverUtils propertyResolverUtils;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The javadoc provider.
|
|
|
+ */
|
|
|
+ private final Optional<JavadocProvider> javadocProvider;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Basic error controller.
|
|
|
+ */
|
|
|
+ private static Class<?> basicErrorController;
|
|
|
+
|
|
|
+ static {
|
|
|
+ try {
|
|
|
+ //spring-boot 2
|
|
|
+ basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController");
|
|
|
+ } catch (ClassNotFoundException e) {
|
|
|
+ //spring-boot 1
|
|
|
+ try {
|
|
|
+ basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.BasicErrorController");
|
|
|
+ } catch (ClassNotFoundException classNotFoundException) {
|
|
|
+ //Basic error controller class not found
|
|
|
+ LOGGER.trace(classNotFoundException.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Instantiates a new Open api builder.
|
|
|
+ *
|
|
|
+ * @param openAPI the open api
|
|
|
+ * @param securityParser the security parser
|
|
|
+ * @param springDocConfigProperties the spring doc config properties
|
|
|
+ * @param propertyResolverUtils the property resolver utils
|
|
|
+ * @param openApiBuilderCustomizers the open api builder customisers
|
|
|
+ * @param serverBaseUrlCustomizers the server base url customizers
|
|
|
+ * @param javadocProvider the javadoc provider
|
|
|
+ */
|
|
|
+ public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
|
|
|
+ SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
|
|
+ Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
|
|
+ Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
|
|
+ Optional<JavadocProvider> javadocProvider) {
|
|
|
+ super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
|
|
+ if (openAPI.isPresent()) {
|
|
|
+ this.openAPI = openAPI.get();
|
|
|
+ if (this.openAPI.getComponents() == null)
|
|
|
+ this.openAPI.setComponents(new Components());
|
|
|
+ if (this.openAPI.getPaths() == null)
|
|
|
+ this.openAPI.setPaths(new Paths());
|
|
|
+ if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
|
|
|
+ this.isServersPresent = true;
|
|
|
+ }
|
|
|
+ this.propertyResolverUtils = propertyResolverUtils;
|
|
|
+ this.securityParser = securityParser;
|
|
|
+ this.springDocConfigProperties = springDocConfigProperties;
|
|
|
+ this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
|
|
+ this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
|
|
+ this.javadocProvider = javadocProvider;
|
|
|
+ if (springDocConfigProperties.isUseFqn())
|
|
|
+ TypeNameResolver.std.setUseFqn(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
|
|
|
+
|
|
|
+ Set<Tag> tags = new HashSet<>();
|
|
|
+ Set<String> tagsStr = new HashSet<>();
|
|
|
+
|
|
|
+ buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
|
|
|
+ buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
|
|
|
+
|
|
|
+ if (!CollectionUtils.isEmpty(tagsStr))
|
|
|
+ tagsStr = tagsStr.stream()
|
|
|
+ .map(str -> propertyResolverUtils.resolve(str, locale))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (springdocTags.containsKey(handlerMethod)) {
|
|
|
+ io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);
|
|
|
+ tagsStr.add(tag.getName());
|
|
|
+ if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
|
|
+ openAPI.addTagsItem(tag);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!CollectionUtils.isEmpty(tagsStr)) {
|
|
|
+ if (CollectionUtils.isEmpty(operation.getTags()))
|
|
|
+ operation.setTags(new ArrayList<>(tagsStr));
|
|
|
+ else {
|
|
|
+ Set<String> operationTagsSet = new HashSet<>(operation.getTags());
|
|
|
+ operationTagsSet.addAll(tagsStr);
|
|
|
+ operation.getTags().clear();
|
|
|
+ operation.getTags().addAll(operationTagsSet);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isAutoTagClasses(operation)) {
|
|
|
+
|
|
|
+
|
|
|
+ if (javadocProvider.isPresent()) {
|
|
|
+ String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
|
|
|
+ if (StringUtils.isNotBlank(description)) {
|
|
|
+ io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag();
|
|
|
+
|
|
|
+ // 自定义部分 修改使用java注释当tag名
|
|
|
+ List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
|
|
|
+ // tag.setName(tagAutoName);
|
|
|
+ tag.setName(list.get(0));
|
|
|
+ operation.addTagsItem(list.get(0));
|
|
|
+
|
|
|
+ tag.setDescription(description);
|
|
|
+ if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
|
|
+ openAPI.addTagsItem(tag);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
|
|
|
+ operation.addTagsItem(tagAutoName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!CollectionUtils.isEmpty(tags)) {
|
|
|
+ // Existing tags
|
|
|
+ List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();
|
|
|
+ if (!CollectionUtils.isEmpty(openApiTags))
|
|
|
+ tags.addAll(openApiTags);
|
|
|
+ openAPI.setTags(new ArrayList<>(tags));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handle SecurityRequirement at operation level
|
|
|
+ io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
|
|
|
+ .getSecurityRequirements(handlerMethod);
|
|
|
+ if (securityRequirements != null) {
|
|
|
+ if (securityRequirements.length == 0)
|
|
|
+ operation.setSecurity(Collections.emptyList());
|
|
|
+ else
|
|
|
+ securityParser.buildSecurityRequirement(securityRequirements, operation);
|
|
|
+ }
|
|
|
+
|
|
|
+ return operation;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
|
|
|
+ // method tags
|
|
|
+ Set<Tags> tagsSet = AnnotatedElementUtils
|
|
|
+ .findAllMergedAnnotations(method, Tags.class);
|
|
|
+ Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
|
|
|
+ .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
|
|
|
+ methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
|
|
|
+ if (!CollectionUtils.isEmpty(methodTags)) {
|
|
|
+ tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
|
|
|
+ List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
|
|
|
+ addTags(allTags, tags, locale);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
|
|
|
+ Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils
|
|
|
+ .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
|
|
|
+ optionalTagSet.ifPresent(tagsSet -> {
|
|
|
+ tagsSet.forEach(tag -> {
|
|
|
+ tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
|
|
|
+ tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
|
|
|
+ if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
|
|
|
+ tags.add(tag);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+}
|