diff --git a/openapi/src/main/java/io/micronaut/openapi/view/OpenApiViewConfig.java b/openapi/src/main/java/io/micronaut/openapi/view/OpenApiViewConfig.java index 1b30a96689..db881b6b36 100644 --- a/openapi/src/main/java/io/micronaut/openapi/view/OpenApiViewConfig.java +++ b/openapi/src/main/java/io/micronaut/openapi/view/OpenApiViewConfig.java @@ -21,6 +21,7 @@ import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.inject.visitor.VisitorContext; +import io.micronaut.openapi.visitor.ConfigUtils; import io.micronaut.openapi.visitor.ContextUtils; import io.micronaut.openapi.visitor.Pair; import io.micronaut.openapi.visitor.group.OpenApiInfo; @@ -43,13 +44,13 @@ import java.util.Optional; import java.util.Properties; -import static io.micronaut.openapi.visitor.ConfigUtils.getConfigProperty; import static io.micronaut.openapi.visitor.ConfigUtils.getProjectPath; import static io.micronaut.openapi.visitor.ContextUtils.addGeneratedResource; import static io.micronaut.openapi.visitor.ContextUtils.info; import static io.micronaut.openapi.visitor.ContextUtils.warn; import static io.micronaut.openapi.visitor.FileUtils.readFile; import static io.micronaut.openapi.visitor.FileUtils.resolve; +import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH; import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_SERVER_CONTEXT_PATH; import static io.micronaut.openapi.visitor.StringUtil.COMMA; import static io.micronaut.openapi.visitor.StringUtil.DOLLAR; @@ -88,7 +89,6 @@ public final class OpenApiViewConfig { private String mappingPath; private String title; private String specFile; - private String serverContextPath = StringUtils.EMPTY_STRING; private SwaggerUIConfig swaggerUIConfig; private RedocConfig redocConfig; private RapidocConfig rapidocConfig; @@ -390,15 +390,6 @@ private void render(AbstractViewConfig cfg, Path outputDir, String templateName, } } - /** - * Sets the server context path. - * - * @param contextPath The server context path. - */ - public void setServerContextPath(String contextPath) { - serverContextPath = contextPath == null ? StringUtils.EMPTY_STRING : contextPath; - } - /** * Returns the title for the generated views. * @@ -434,26 +425,31 @@ public String getSpecURL(AbstractViewConfig cfg, @Nullable VisitorContext contex return StringUtils.EMPTY_STRING; } - String specUrl = StringUtils.prependUri(serverContextPath, StringUtils.prependUri(mappingPath, specFile)); - if (StringUtils.isEmpty(serverContextPath)) { - String contextPath = getConfigProperty(MICRONAUT_SERVER_CONTEXT_PATH, context); - if (contextPath == null) { - contextPath = StringUtils.EMPTY_STRING; - } - if (!contextPath.startsWith(SLASH) && !contextPath.startsWith(DOLLAR)) { - contextPath = SLASH + contextPath; - } - if (!contextPath.endsWith(SLASH)) { - contextPath += SLASH; - } - if (specUrl.startsWith(SLASH)) { - specUrl = specUrl.substring(1); - } + // process micronaut.openapi.server.context.path + String serverContextPath = ConfigUtils.getConfigProperty(MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, context); + if (serverContextPath == null) { + serverContextPath = StringUtils.EMPTY_STRING; + } + String finalUrl = serverContextPath.startsWith(SLASH) ? serverContextPath : SLASH + serverContextPath; + if (!finalUrl.endsWith(SLASH)) { + finalUrl += SLASH; + } - specUrl = contextPath + specUrl; + // process micronaut.server.context-path + String contextPath = ConfigUtils.getConfigProperty(MICRONAUT_SERVER_CONTEXT_PATH, context); + if (contextPath == null) { + contextPath = StringUtils.EMPTY_STRING; + } + finalUrl += contextPath.startsWith(SLASH) ? contextPath.substring(1) : contextPath; + if (!finalUrl.endsWith(SLASH)) { + finalUrl += SLASH; } - return specUrl; + finalUrl = StringUtils.prependUri(finalUrl, StringUtils.prependUri(mappingPath, specFile)); + if (!finalUrl.startsWith(SLASH) && !finalUrl.startsWith(DOLLAR)) { + finalUrl = SLASH + finalUrl; + } + return finalUrl; } /** diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java index 144eea1567..ea3d3540f8 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java @@ -124,8 +124,8 @@ Map> resolvePathItems(VisitorContext context, List>(); for (UriMatchTemplate matchTemplate : matchTemplates) { - var segms = parsePathSegments(matchTemplate.toPathString()); - var finalPaths = buildUrls(segms); + var segments = parsePathSegments(matchTemplate.toPathString()); + var finalPaths = buildUrls(segments, context); for (String finalPath : finalPaths) { List resultPathItems = resultPathItemsMap.computeIfAbsent(finalPath, k -> new ArrayList<>()); diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java index 89d6c28571..8bd10e082e 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java @@ -275,7 +275,6 @@ private void renderViews(String title, Map, OpenApiInfo> op if (CollectionUtils.isNotEmpty(openApiInfos)) { cfg.setSpecFile(openApiInfos.values().iterator().next().getSpecFilePath()); } - cfg.setServerContextPath(getConfigProperty(MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, context)); cfg.render(destinationDir, context); } } diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/UrlUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/UrlUtils.java index ed01dd07db..25bdff9770 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/UrlUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/UrlUtils.java @@ -16,10 +16,13 @@ package io.micronaut.openapi.visitor; import io.micronaut.core.annotation.Internal; +import io.micronaut.core.util.StringUtils; +import io.micronaut.inject.visitor.VisitorContext; import java.util.ArrayList; import java.util.List; +import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_SERVER_CONTEXT_PATH; import static io.micronaut.openapi.visitor.StringUtil.CLOSE_BRACE; import static io.micronaut.openapi.visitor.StringUtil.DOLLAR; import static io.micronaut.openapi.visitor.StringUtil.OPEN_BRACE; @@ -47,7 +50,7 @@ private UrlUtils() { * @param segments url template segments * @return all possible URL variants by parsed segments. */ - public static List buildUrls(List segments) { + public static List buildUrls(List segments, VisitorContext context) { var results = new ArrayList(); @@ -57,6 +60,16 @@ public static List buildUrls(List segments) { prevSegment = segment; } + String contextPath = ConfigUtils.getConfigProperty(MICRONAUT_SERVER_CONTEXT_PATH, context); + if (StringUtils.isNotEmpty(contextPath)) { + if (!contextPath.startsWith(SLASH) && !contextPath.startsWith(DOLLAR)) { + contextPath = SLASH + contextPath; + } + if (contextPath.endsWith(SLASH)) { + contextPath = contextPath.substring(0, contextPath.length() - 1); + } + } + var resultStrings = new ArrayList(); for (var res : results) { var url = res.toString(); @@ -67,6 +80,11 @@ public static List buildUrls(List segments) { } else if (url.startsWith(SLASH + DOLLAR)) { url = url.substring(1); } + + if (StringUtils.isNotEmpty(contextPath)) { + url = contextPath + url; + } + if (!resultStrings.contains(url)) { resultStrings.add(url); } diff --git a/openapi/src/test/groovy/io/micronaut/openapi/view/OpenApiOperationViewRenderSpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/view/OpenApiOperationViewRenderSpec.groovy index 38d73a10ac..d4c2230b98 100755 --- a/openapi/src/test/groovy/io/micronaut/openapi/view/OpenApiOperationViewRenderSpec.groovy +++ b/openapi/src/test/groovy/io/micronaut/openapi/view/OpenApiOperationViewRenderSpec.groovy @@ -11,6 +11,7 @@ import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import spock.util.environment.RestoreSystemProperties class OpenApiOperationViewRenderSpec extends Specification { def cleanup() { @@ -168,14 +169,15 @@ class OpenApiOperationViewRenderSpec extends Specification { outputDir.resolve("swagger-ui").resolve("index.html").toFile().getText(StandardCharsets.UTF_8.name()).contains("link(contextPath + \"https://flattop.com/theme.css\", head, \"text/css\", \"stylesheet\")") } + @RestoreSystemProperties void "test render OpenApiView specification with server context path"() { given: + System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, "/context-path") String spec = "redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,openapi-explorer.enabled=true" OpenApiViewConfig cfg = OpenApiViewConfig.fromSpecification(spec, null, new Properties(), null) Path outputDir = Paths.get("output") cfg.title = "OpenAPI documentation" cfg.specFile = "swagger.yml" - cfg.serverContextPath = "/context-path" cfg.render(outputDir, null) expect: @@ -233,14 +235,15 @@ class OpenApiOperationViewRenderSpec extends Specification { outputDir.resolve("openapi-explorer").resolve("index.html").toFile().getText(StandardCharsets.UTF_8.name()).contains(cfg.getSpecURL(cfg.openApiExplorerConfig, null)) } + @RestoreSystemProperties void "test render OpenApiView specification with custom mapping path and server context path"() { given: + System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, "/context-path") String spec = "mapping.path=somewhere,redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,openapi-explorer.enabled=true" OpenApiViewConfig cfg = OpenApiViewConfig.fromSpecification(spec, null, new Properties(), null) Path outputDir = Paths.get("output") cfg.title = "OpenAPI documentation" cfg.specFile = "swagger.yml" - cfg.serverContextPath = "/context-path" cfg.render(outputDir, null) expect: diff --git a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiControllerVisitorSpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiControllerVisitorSpec.groovy index b02ed99369..8ee8356372 100644 --- a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiControllerVisitorSpec.groovy +++ b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiControllerVisitorSpec.groovy @@ -8,6 +8,7 @@ import io.swagger.v3.oas.models.PathItem import io.swagger.v3.oas.models.Paths import io.swagger.v3.oas.models.media.Schema import spock.lang.Issue +import spock.util.environment.RestoreSystemProperties class OpenApiControllerVisitorSpec extends AbstractOpenApiTypeElementSpec { @@ -2459,4 +2460,41 @@ class MyBean {} operation.requestBody.content."application/json".schema operation.requestBody.content."application/json".schema.$ref == "#/components/schemas/SimpleBody" } + + @RestoreSystemProperties + void "test append micronaut.server.context-path to endpoints"() { + given: + System.setProperty(OpenApiConfigProperty.MICRONAUT_SERVER_CONTEXT_PATH, "/local-path") + System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, "/server-context-path") + + buildBeanDefinition('test.MyBean', ''' +package test; + +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import jakarta.inject.Singleton; + +@Controller("/test") +class TestController { + + @Get("/save{/id}") + String save() { + return null; + } +} + +@Singleton +class MyBean {} +''') + when: + OpenAPI openAPI = Utils.testReference + def paths = openAPI.paths + + then: + paths + paths."/server-context-path/local-path/test/save" + paths."/server-context-path/local-path/test/save".get + paths."/server-context-path/local-path/test/save/{id}" + paths."/server-context-path/local-path/test/save/{id}".get + } }