Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
09546c7
Add "location-path" to swagger-ui
Dec 7, 2024
840057e
Merge branch 'main' into swagger
Mar 5, 2025
067834f
Add "location-path" to swagger-ui
Mar 5, 2025
d043978
Merge branch 'main' into swagger
alexey-plotnikoff Mar 5, 2025
8ebcae6
Code review fixes
Mar 10, 2025
c698015
Merge remote-tracking branch 'origin/swagger' into swagger
Mar 10, 2025
a6a817e
Merge branch 'main' into swagger
alexey-plotnikoff Mar 10, 2025
4005c90
Merge branch 'main' into swagger
alexey-plotnikoff Mar 11, 2025
2e3e4b5
Code review fixes
Mar 11, 2025
251bef9
Merge branch 'main' into swagger
alexey-plotnikoff Mar 11, 2025
7317647
Code review fixes
Mar 12, 2025
c87627f
Merge remote-tracking branch 'origin/swagger' into swagger
Mar 12, 2025
89cc368
fix import sort
Mar 14, 2025
4081018
Merge branch 'main' into swagger
alexey-plotnikoff Mar 14, 2025
43af23e
Merge branch 'main' into swagger
alexey-plotnikoff Mar 15, 2025
44970a3
Add "location-path" to swagger-ui
Dec 7, 2024
b7c8825
Add "location-path" to swagger-ui
Mar 5, 2025
bc6018d
Code review fixes
Mar 10, 2025
0d7353b
Code review fixes
Mar 11, 2025
c3e7ab7
fix import sort
Mar 14, 2025
b4b0af7
Merge remote-tracking branch 'origin/swagger' into swagger
Feb 16, 2026
9a1b1b1
rebase
Feb 16, 2026
6c75549
Merge branch 'main' into swagger
alexey-plotnikoff Feb 16, 2026
083be9b
fix import
Feb 16, 2026
0ff0029
Merge branch 'main' into swagger
alexey-plotnikoff Feb 16, 2026
614984c
Merge branch 'main' into swagger
alexey-plotnikoff Feb 17, 2026
ddc6831
Merge branch 'main' into swagger
alexey-plotnikoff Feb 17, 2026
5ef855b
Merge branch 'main' into swagger
alexey-plotnikoff Mar 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,9 @@ public interface SwaggerUiConfig {
*/
@WithDefault("false")
boolean tryItOutEnabled();

/**
* Custom root path. Useful when the application is behind a proxy with a custom root path.
*/
Optional<String> rootPath();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -27,6 +28,7 @@
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.util.UriNormalizationUtil;
import io.quarkus.devui.deployment.menu.EndpointsProcessor;
import io.quarkus.maven.dependency.GACT;
import io.quarkus.runtime.configuration.ConfigurationException;
Expand Down Expand Up @@ -108,7 +110,17 @@ public void getSwaggerUiFinalDestination(

}

String openApiPath = nonApplicationRootPathBuildItem.resolvePath(openapi.path());
String openApiPath;
if (swaggerUiConfig.rootPath().isPresent()) {
URI root = UriNormalizationUtil.toURI("/", true);
openApiPath = UriNormalizationUtil.normalizeWithBase(
root,
swaggerUiConfig.rootPath().get()
+ nonApplicationRootPathBuildItem.resolvePath(openapi.path()),
false).getPath();
} else {
openApiPath = nonApplicationRootPathBuildItem.resolvePath(openapi.path());
}

String swaggerUiPath = nonApplicationRootPathBuildItem.resolvePath(swaggerUiConfig.path());
ThemeHref theme = swaggerUiConfig.theme().orElse(ThemeHref.feeling_blue);
Expand Down Expand Up @@ -380,6 +392,12 @@ private byte[] generateIndexHtml(String openApiPath, String swaggerUiPath, Swagg
if (swaggerUiConfig.queryConfigEnabled()) {
options.put(Option.queryConfigEnabled, "true");
}
if (swaggerUiConfig.rootPath().isPresent()) {
var rootPath = swaggerUiConfig.rootPath().get();
addRootPath(rootPath, Option.selfHref, options);
addRootPath(rootPath, Option.backHref, options);
addRootPath(rootPath, Option.oauth2RedirectUrl, options);
}
Comment on lines +421 to +426
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My problem with how it's implemented is that the value is fixed at build time.

Will it fly in standard setups? (Honest question, I don't have an answer)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question.

@phillip-kruger what do you think?

Copy link
Author

@alexey-plotnikoff alexey-plotnikoff Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @gsmet but we have same problem for all variables in indexHtmlContent:

  1. openApiPath = nonApplicationRootPathBuildItem.resolvePath(openapi.path());
  2. swaggerUiPath
  3. swaggerUiConfig
  4. launchMode
  5. devServicesLauncherConfigResultBuildItem

Maybe we should fix this in another PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point swagger ui html is generated at build time. allowing dynamic creation at runtime will be a big change. We can maybe change the static generated html and js to allow some parameters that can change behavior at runtime, but even that is a much bigger change


ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> oauthAdditionalQueryStringParamMap = new HashMap<>();
Expand Down Expand Up @@ -439,6 +457,16 @@ private byte[] generateIndexHtml(String openApiPath, String swaggerUiPath, Swagg
return IndexHtmlCreator.createIndexHtml(urlsMap, swaggerUiConfig.urlsPrimaryName().orElse(null), options);
}

private void addRootPath(String rootPath, Option key, Map<Option, String> options) {
var value = options.get(key);
if (value != null) {
URI root = UriNormalizationUtil.toURI("/", true);
var behindRootPath = UriNormalizationUtil.normalizeWithBase(root, rootPath + value, false)
.getPath();
options.put(key, behindRootPath);
}
}

private static boolean shouldInclude(LaunchModeBuildItem launchMode, SwaggerUiConfig swaggerUiConfig) {
return launchMode.getLaunchMode().isDevOrTest() || swaggerUiConfig.alwaysInclude();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.swaggerui.deployment;

import static org.hamcrest.Matchers.containsString;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class RootPathTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource(new StringAsset(
"quarkus.swagger-ui.root-path=/nginx/path"), "application.properties"));

@Test
public void shouldUseCustomRootPath() {
RestAssured.when().get("/q/swagger-ui").then().statusCode(200).body(containsString("/nginx/path/q/openapi"));
RestAssured.when().get("/q/swagger-ui/index.html").then().statusCode(200).body(containsString("/nginx/path/q/openapi"));
RestAssured.when().get("/q/swagger-ui").then()
.statusCode(200)
.body(containsString("/nginx/path/q/swagger-ui/oauth2-redirect.html"));
RestAssured.when().get("/q/swagger-ui/index.html").then()
.statusCode(200)
.body(containsString("/nginx/path/q/swagger-ui/oauth2-redirect.html"));
RestAssured.when().get("/q/swagger-ui").then()
.statusCode(200)
.body(containsString("/nginx/path/q/swagger-ui"));
RestAssured.when().get("/q/swagger-ui/index.html").then()
.statusCode(200)
.body(containsString("/nginx/path/q/swagger-ui"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public String getNonApplicationRootPath() {
}

/**
* @return the normalized root path for the mangement endpoints. {@code getNonApplicationRootPath()} if the
* @return the normalized root path for the management endpoints. {@code getNonApplicationRootPath()} if the
* management interface is disabled.
*/
public String getManagementRootPath() {
Expand Down
Loading