From 99165448269446abc3dca351e66f7f70588d616c Mon Sep 17 00:00:00 2001 From: Jason McIntosh Date: Thu, 18 Jul 2024 14:44:09 -0500 Subject: [PATCH] feat(swagger): Upgrade to swagger and migrate to springdocs --- kork-swagger/kork-swagger.gradle | 7 +- ...ngfoxHandlerProviderBeanPostProcessor.java | 77 ------------------- .../spinnaker/config/SwaggerConfig.java | 59 +++++--------- .../main/resources/META-INF/spring.factories | 1 - ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../main/resources/static/swagger/index.html | 17 ---- .../spinnaker-dependencies.gradle | 12 ++- 7 files changed, 27 insertions(+), 147 deletions(-) delete mode 100644 kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java delete mode 100644 kork-swagger/src/main/resources/META-INF/spring.factories delete mode 100644 kork-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 kork-swagger/src/main/resources/static/swagger/index.html diff --git a/kork-swagger/kork-swagger.gradle b/kork-swagger/kork-swagger.gradle index 8d5118bed..6ba23f97f 100644 --- a/kork-swagger/kork-swagger.gradle +++ b/kork-swagger/kork-swagger.gradle @@ -3,11 +3,10 @@ apply plugin: "java-library" dependencies { api(platform(project(":spinnaker-dependencies"))) - api "io.swagger:swagger-annotations" - implementation "com.google.guava:guava" implementation "org.springframework.boot:spring-boot-autoconfigure" - implementation "io.springfox:springfox-boot-starter" + implementation "org.springdoc:springdoc-openapi-ui" implementation "org.springframework:spring-webmvc" - + implementation "io.swagger.core.v3:swagger-annotations" + implementation "io.swagger.core.v3:swagger-core" } diff --git a/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java b/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java deleted file mode 100644 index de624674e..000000000 --- a/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2024 OpsMx, Inc. - * - * 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 com.netflix.spinnaker.config; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.stereotype.Component; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; -import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; - -/** - * With spring boot 2.6.x, default strategy for matching request paths against registered Spring MVC - * handler mappings has changed from AntPathMatcher to PathPatternParser. The actuator endpoints - * also use PathPattern based URL matching and path matching strategy cannot be configured for - * actuator endpoints via a configuration property. Using Actuator and Springfox may result in - * application failing to start. {@see https://github.com/springfox/springfox/issues/3462} - */ -@Component -public class SpringfoxHandlerProviderBeanPostProcessor implements BeanPostProcessor { - - /** - * Overrides BeanPostProcessor method postProcessAfterInitialization() to filter the - * springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider beans for request - * mapping. - * - * @param bean – the new bean instance - * @param beanName – the name of the bean - * @return the bean instance to use, either the original or a wrapped one; if null, no subsequent - * BeanPostProcessors will be invoked - */ - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof WebMvcRequestHandlerProvider) { - customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); - } - return bean; - } - - private void customizeSpringfoxHandlerMappings( - List mappings) { - List copy = - mappings.stream() - .filter(mapping -> mapping.getPatternParser() == null) - .collect(Collectors.toList()); - mappings.clear(); - mappings.addAll(copy); - } - - @SuppressWarnings("unchecked") - private List getHandlerMappings(Object bean) { - try { - Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); - field.setAccessible(true); - return (List) field.get(bean); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new IllegalStateException(e); - } - } -} diff --git a/kork-swagger/src/main/java/com/netflix/spinnaker/config/SwaggerConfig.java b/kork-swagger/src/main/java/com/netflix/spinnaker/config/SwaggerConfig.java index 662e45b2f..eb82cf5ff 100644 --- a/kork-swagger/src/main/java/com/netflix/spinnaker/config/SwaggerConfig.java +++ b/kork-swagger/src/main/java/com/netflix/spinnaker/config/SwaggerConfig.java @@ -16,21 +16,19 @@ package com.netflix.spinnaker.config; -import com.google.common.collect.ImmutableList; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.function.Predicate; import javax.annotation.Nullable; +import org.springdoc.core.SpringDocUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.paths.DefaultPathProvider; -import springfox.documentation.spring.web.plugins.Docket; @Configuration @ConditionalOnProperty("swagger.enabled") @@ -43,19 +41,21 @@ public class SwaggerConfig { private String basePath = ""; private String documentationPath = "/"; - private static final ImmutableList IGNORED_CLASS_NAMES = - ImmutableList.of("groovy.lang.MetaClass"); + private static final List IGNORED_CLASS_NAMES = List.of("groovy.lang.MetaClass"); @Bean - public Docket gateApi() { - return new Docket(DocumentationType.SWAGGER_2) - .pathProvider(new BasePathProvider(documentationPath)) - .select() - .apis(RequestHandlerSelectors.any()) - .paths(paths()) - .build() - .apiInfo(apiInfo()) - .ignoredParameterTypes(ignoredClasses()); + public OpenAPI gateApi() { + Arrays.stream((ignoredClasses())) + .forEach( + each -> { + SpringDocUtils.getConfig().addJavaTypeToIgnore(each); + }); + return new OpenAPI() + .info(new Info().description(description).title(title).contact(new Contact().name(contact))) + .externalDocs( + new ExternalDocumentation() + .url("https://spinnaker.io") + .description("Spinnaker Documentation")); } private static Class[] ignoredClasses() { @@ -74,14 +74,6 @@ private static Class getClassIfPresent(String name) { } } - private Predicate paths() { - return patterns.stream().map(PathSelectors::regex).reduce((x, y) -> x.or(y)).get(); - } - - private ApiInfo apiInfo() { - return new ApiInfo(title, description, null, null, contact, null, null); - } - public void setTitle(String title) { this.title = title; } @@ -117,17 +109,4 @@ public void setDocumentationPath(String documentationPath) { public String getDocumentationPath() { return documentationPath; } - - public class BasePathProvider extends DefaultPathProvider { - private String documentationPath; - - private BasePathProvider(String documentationPath) { - this.documentationPath = documentationPath; - } - - @Override - protected String getDocumentationPath() { - return documentationPath; - } - } } diff --git a/kork-swagger/src/main/resources/META-INF/spring.factories b/kork-swagger/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 8b1378917..000000000 --- a/kork-swagger/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ - diff --git a/kork-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/kork-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index e83bab705..000000000 --- a/kork-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.netflix.spinnaker.config.SpringfoxHandlerProviderBeanPostProcessor diff --git a/kork-swagger/src/main/resources/static/swagger/index.html b/kork-swagger/src/main/resources/static/swagger/index.html deleted file mode 100644 index 0d0ddcd56..000000000 --- a/kork-swagger/src/main/resources/static/swagger/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - Swagger Redirect - - - -

- The contents you are looking for have moved. -

-

- You will be redirected to the new location automatically in 5 seconds. -

-

- Please bookmark the correct page at /swagger-ui.html -

- - diff --git a/spinnaker-dependencies/spinnaker-dependencies.gradle b/spinnaker-dependencies/spinnaker-dependencies.gradle index 6c4216645..efb976f2f 100644 --- a/spinnaker-dependencies/spinnaker-dependencies.gradle +++ b/spinnaker-dependencies/spinnaker-dependencies.gradle @@ -21,7 +21,7 @@ ext { protobuf : "3.21.12", okhttp : "2.7.5", // CVE-2016-2402 okhttp3 : "4.9.3", - openapi : "1.3.9", // this needs to be kept in sync with spring boot as it pulls in the spring-boot-dependencies BOM + openapi : "1.8.0", retrofit : "1.9.0", retrofit2 : "2.8.1", spectator : "1.0.6", @@ -29,9 +29,7 @@ ext { spek2 : "2.0.9", springBoot : "2.7.18", springCloud : "2021.0.8", - springfoxSwagger : "3.0.0", - swagger : "1.5.20", //this should stay in sync with what springfoxSwagger expects. - + swagger : "2.2.22", // 2.7.18 brings in 9.0.83, which fixes all CVEs to date (20-feb-24). Continue to pin. // See https://tomcat.apache.org/security-9.html for latest security fixes. tomcat : "9.0.83" @@ -143,9 +141,8 @@ dependencies { api("de.huxhorn.sulky:de.huxhorn.sulky.ulid:8.2.0") api("dev.minutest:minutest:1.13.0") api("io.mockk:mockk:1.10.5") - api("io.springfox:springfox-boot-starter:${versions.springfoxSwagger}") - api("io.springfox:springfox-swagger2:${versions.springfoxSwagger}") - api("io.swagger:swagger-annotations:${versions.swagger}") + api("io.swagger.core.v3:swagger-annotations:${versions.swagger}") + api("io.swagger.core.v3:swagger-core:${versions.swagger}") api("javax.annotation:javax.annotation-api:1.3.2") api("javax.xml.bind:jaxb-api:2.3.1") api("net.logstash.logback:logstash-logback-encoder:4.11") @@ -197,6 +194,7 @@ dependencies { api("org.yaml:snakeyaml:1.31") api("org.springdoc:springdoc-openapi-webmvc-core:${versions.openapi}") api("org.springdoc:springdoc-openapi-kotlin:${versions.openapi}") + api("org.springdoc:springdoc-openapi-ui:${versions.openapi}") api("org.springframework.boot:spring-boot-configuration-processor:${versions.springBoot}") api("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.3.12.RELEASE") api("org.springframework.security.extensions:spring-security-saml-dsl-core:1.0.5.RELEASE")