diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/OpenAPIService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/OpenAPIService.java index bffc25527..2fecc869e 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/OpenAPIService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/OpenAPIService.java @@ -62,7 +62,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; - +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; @@ -120,6 +120,11 @@ public class OpenAPIService implements ApplicationContextAware { */ private final Optional> openApiBuilderCustomisers; + /** + * The server base URL customisers. + */ + private final Optional> serverBaseUrlCustomisers; + /** * The Spring doc config properties. */ @@ -188,7 +193,8 @@ public class OpenAPIService implements ApplicationContextAware { */ OpenAPIService(Optional openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, - Optional> openApiBuilderCustomisers) { + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers) { if (openAPI.isPresent()) { this.openAPI = openAPI.get(); if (this.openAPI.getComponents() == null) @@ -202,6 +208,7 @@ public class OpenAPIService implements ApplicationContextAware { this.securityParser = securityParser; this.springDocConfigProperties = springDocConfigProperties; this.openApiBuilderCustomisers = openApiBuilderCustomisers; + this.serverBaseUrlCustomisers = serverBaseUrlCustomisers; if (springDocConfigProperties.isUseFqn()) TypeNameResolver.std.setUseFqn(true); } @@ -466,7 +473,15 @@ public Schema resolveProperties(Schema schema, Locale locale) { * @param serverBaseUrl the server base url */ public void setServerBaseUrl(String serverBaseUrl) { - this.serverBaseUrl = serverBaseUrl; + String customServerBaseUrl = serverBaseUrl; + + if (serverBaseUrlCustomisers != null && serverBaseUrlCustomisers.isPresent()) { + for (ServerBaseUrlCustomizer customiser : serverBaseUrlCustomisers.get()) { + customServerBaseUrl = customiser.customise(customServerBaseUrl); + } + } + + this.serverBaseUrl = customServerBaseUrl; } /** @@ -808,8 +823,17 @@ public SecurityService getSecurityParser() { return securityParser; } + /** + * Gets server base URL + * + * @return the server base URL + */ + public String getServerBaseUrl() { + return serverBaseUrl; + } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } -} +} \ No newline at end of file diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java index 6a3f89b8e..ba2c0f21f 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java @@ -53,6 +53,7 @@ import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.PropertyCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.providers.ActuatorProvider; import org.springdoc.core.providers.CloudFunctionProvider; import org.springdoc.core.providers.JavadocProvider; @@ -225,8 +226,9 @@ PolymorphicModelConverter polymorphicModelConverter() { OpenAPIService openAPIBuilder(Optional openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties,PropertyResolverUtils propertyResolverUtils, - Optional> openApiBuilderCustomisers) { - return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers); + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers) { + return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers); } /** @@ -579,4 +581,4 @@ CloudFunctionProvider springCloudFunctionProvider(Optional func return new SpringCloudFunctionProvider(functionCatalog, genericResponseService, springDocConfigProperties); } } -} +} \ No newline at end of file diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java new file mode 100644 index 000000000..ad7e04d93 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java @@ -0,0 +1,17 @@ +package org.springdoc.core.customizers; + +/** + * The interface Server Base URL customiser. + * @author skylar-stark + */ +@FunctionalInterface +public interface ServerBaseUrlCustomizer { + + /** + * Customise. + * + * @param serverBaseUrl the serverBaseUrl. + * @return the customised serverBaseUrl + */ + public String customise(String serverBaseUrl); +} diff --git a/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java b/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java index a3bb0db70..207d9ce45 100644 --- a/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java +++ b/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java @@ -20,6 +20,7 @@ package org.springdoc.api; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -49,6 +50,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.fn.RouterOperation; @@ -111,6 +113,7 @@ public void setUp() { openAPI = new OpenAPI(); openAPI.setPaths(new Paths().addPathItem(PATH, new PathItem())); ReflectionTestUtils.setField(openAPIService, "cachedOpenAPI", new HashMap<>()); + ReflectionTestUtils.setField(openAPIService, "serverBaseUrlCustomisers", Optional.empty()); when(openAPIService.getCalculatedOpenAPI()).thenReturn(openAPI); when(openAPIService.getContext()).thenReturn(context); @@ -215,6 +218,71 @@ void preLoadingModeShouldNotOverwriteServers() throws InterruptedException { assertThat(after.getServers().get(0).getUrl(), is(customUrl)); } + @Test + void serverBaseUrlCustomisersTest() throws InterruptedException { + when(openAPIService.updateServers(any())).thenCallRealMethod(); + when(openAPIService.getCachedOpenAPI(any())).thenCallRealMethod(); + doAnswer(new CallsRealMethods()).when(openAPIService).setServerBaseUrl(any()); + doAnswer(new CallsRealMethods()).when(openAPIService).setCachedOpenAPI(any(), any()); + + SpringDocConfigProperties properties = new SpringDocConfigProperties(); + properties.setPreLoadingEnabled(true); + + resource = new EmptyPathsOpenApiResource( + GROUP_NAME, + openAPIBuilderObjectFactory, + requestBuilder, + responseBuilder, + operationParser, + Optional.empty(), + Optional.empty(), + Optional.empty(), + properties, + springDocProviders + ); + + // wait for executor to be done + Thread.sleep(1_000); + + Locale locale = Locale.US; + + // Test that setting generated URL works fine with no customisers present + String generatedUrl = "https://generated-url.com/context-path"; + openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.updateServers(openAPI); + OpenAPI after = resource.getOpenApi(locale); + assertThat(after.getServers().get(0).getUrl(), is(generatedUrl)); + + // Test that adding a serverBaseUrlCustomiser has the desired effect + ServerBaseUrlCustomizer serverBaseUrlCustomiser = serverBaseUrl -> serverBaseUrl.replace("/context-path", ""); + List serverBaseUrlCustomiserList = new ArrayList<>(); + serverBaseUrlCustomiserList.add(serverBaseUrlCustomiser); + + ReflectionTestUtils.setField(openAPIService, "serverBaseUrlCustomisers", Optional.of(serverBaseUrlCustomiserList)); + openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.updateServers(openAPI); + after = resource.getOpenApi(locale); + assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com")); + + // Test that serverBaseUrlCustomisers are performed in order + generatedUrl = "https://generated-url.com/context-path/second-path"; + ServerBaseUrlCustomizer serverBaseUrlCustomiser2 = serverBaseUrl -> serverBaseUrl.replace("/context-path/second-path", ""); + serverBaseUrlCustomiserList.add(serverBaseUrlCustomiser2); + + openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.updateServers(openAPI); + after = resource.getOpenApi(locale); + assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com/second-path")); + + // Test that all serverBaseUrlCustomisers in the List are performed + ServerBaseUrlCustomizer serverBaseUrlCustomiser3 = serverBaseUrl -> serverBaseUrl.replace("/second-path", ""); + serverBaseUrlCustomiserList.add(serverBaseUrlCustomiser3); + + openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.updateServers(openAPI); + after = resource.getOpenApi(locale); + assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com")); + } private static class EmptyPathsOpenApiResource extends AbstractOpenApiResource {