io.quarkus
quarkus-swagger-ui
diff --git a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java
new file mode 100644
index 0000000000000..61f39b32eefb1
--- /dev/null
+++ b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java
@@ -0,0 +1,438 @@
+package io.quarkus.swaggerui.deployment;
+
+import java.net.URI;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+
+import io.quarkus.runtime.annotations.ConfigGroup;
+import io.quarkus.runtime.annotations.ConfigItem;
+import io.quarkus.runtime.annotations.ConfigRoot;
+import io.vertx.core.http.HttpMethod;
+
+/**
+ * Please @see Swagger UI documentation
+ */
+@ConfigRoot
+class SwaggerUiConfig {
+
+ /**
+ * If Swagger UI should be enabled. By default, Swagger UI is enabled.
+ */
+ @ConfigItem(defaultValue = "true")
+ boolean enable;
+
+ /**
+ * The path where Swagger UI is available.
+ *
+ * The value `/` is not allowed as it blocks the application from serving anything else.
+ */
+ @ConfigItem(defaultValue = "/swagger-ui")
+ String path;
+
+ /**
+ * If this should be included every time. By default, this is only included when the application is running
+ * in dev mode.
+ */
+ @ConfigItem
+ boolean alwaysInclude;
+
+ /**
+ * URI to fetch external configuration document from.
+ */
+ @ConfigItem
+ Optional configUrl;
+
+ /**
+ * Enables deep linking for tags and operations.
+ *
+ * Please @see Swagger UI deep linking
+ * documentation
+ */
+ @ConfigItem
+ boolean deepLinking;
+
+ /**
+ * The default expansion depth for models (set to -1 completely hide the models).
+ */
+ @ConfigItem(defaultValue = "1")
+ int defaultModelsExpandDepth;
+
+ /**
+ * The default expansion depth for the model in the model-example section.
+ */
+ @ConfigItem(defaultValue = "1")
+ int defaultModelExpandDepth;
+
+ /**
+ * Controls how the model is shown when the API is first rendered.
+ */
+ @ConfigItem(defaultValue = "EXAMPLE")
+ Rendering defaultModelRendering;
+
+ /**
+ * Controls the display of operationId in operations list.
+ */
+ @ConfigItem
+ boolean displayOperationId;
+
+ /**
+ * Controls the display of the request duration (in milliseconds) for Try-It-Out requests.
+ */
+ @ConfigItem
+ boolean displayRequestDuration;
+
+ /**
+ * Controls the default expansion setting for the operations and tags.
+ */
+ @ConfigItem(defaultValue = "LIST")
+ Expansion docExpansion;
+
+ /**
+ * If set, enables filtering. The top bar will show an edit box that could be used to filter
+ * the tagged operations that are shown.
+ */
+ @ConfigItem
+ boolean filter;
+ /**
+ * The name of a component available via the plugin system to use as the top-level layout for Swagger UI.
+ */
+ @ConfigItem(defaultValue = "StandaloneLayout")
+ String layout;
+
+ /**
+ * If set, limits the number of tagged operations displayed to at most this many.
+ */
+ @ConfigItem
+ OptionalInt maxDisplayedTags;
+
+ /**
+ * Controls the display of vendor extension (x-) fields and values.
+ */
+ @ConfigItem
+ boolean showExtensions;
+
+ /**
+ * Controls the display of extensions.
+ */
+ @ConfigItem
+ boolean showCommonExtensions;
+
+ /**
+ * The supported try it out methods.
+ */
+ @ConfigItem
+ Optional> supportedSubmitMethods;
+
+ /**
+ * Apply a sort to the operation list of each API.
+ */
+ @ConfigItem
+ Optional operationsSorter;
+
+ /**
+ * Apply a sort to the tag list of each API.
+ */
+ @ConfigItem
+ Optional tagsSorter;
+
+ /**
+ * The url pointing to API definition (the default points to the URI generated by the OpenAPI extension).
+ */
+ @ConfigItem
+ Optional url;
+
+ /**
+ * The order of the swagger groups which will be displayed when Swagger UI loads.
+ */
+ @ConfigItem(defaultValue = "ASC")
+ Order groupsOrder;
+
+ /**
+ * When used and Topbar plugin is enabled, the url parameter will not be parsed.
+ * Names and URLs must be unique among all items in this array, since they're used as identifiers.
+ * SpecGroupUrl must match the pattern: ?group=
+ */
+ @ConfigItem
+ Optional> urls;
+
+ /**
+ * The name of the swagger group which will be displayed when Swagger UI loads.
+ */
+ @ConfigItem
+ Optional urlsPrimaryName;
+
+ /**
+ * Set a different validator URL, for example for locally deployed validators.
+ */
+ @ConfigItem
+ Optional validatorUrl;
+
+ /**
+ * OAuth redirect URL.
+ */
+ @ConfigItem
+ Optional oauth2RedirectUrl;
+
+ /**
+ * OAuth configuration.
+ */
+ OAuthConfig oauth;
+
+ public Map getSwaggerUiProps(final String openApiPath) {
+ updateApiUrl(openApiPath);
+ updateAuthUrl();
+ updateLayout();
+ return getProps();
+ }
+
+ private Map getProps() {
+ Map props = new TreeMap<>();
+ props.put("deepLinking", deepLinking);
+ props.put("defaultModelExpandDepth", defaultModelExpandDepth);
+ props.put("defaultModelRendering", defaultModelRendering.toString().toLowerCase());
+ props.put("defaultModelsExpandDepth", defaultModelsExpandDepth);
+ props.put("displayOperationId", displayOperationId);
+ props.put("displayRequestDuration", displayRequestDuration);
+ props.put("docExpansion", docExpansion.toString().toLowerCase());
+ props.put("dom_id", "#swagger-ui");
+ props.put("filter", filter);
+ maxDisplayedTags.ifPresent(i -> props.put("maxDisplayedTags", i));
+ props.put("layout", layout);
+ oauth2RedirectUrl.ifPresent(s -> props.put("oauth2RedirectUrl", s));
+ operationsSorter.ifPresent(s -> props.put("operationsSorter", s.toString().toLowerCase()));
+ props.put("showCommonExtensions", showCommonExtensions);
+ props.put("showExtensions", showExtensions);
+ supportedSubmitMethods.ifPresent(ls -> props.put("supportedSubmitMethods", getSupportedSubmitMethods(ls)));
+ tagsSorter.ifPresent(s -> props.put("tagsSorter", s.toString().toLowerCase()));
+ url.ifPresent(s -> props.put("url", s));
+ url.ifPresent(s -> props.put("url", s));
+ urls.ifPresent(sgu -> props.put("urls", getSpecGroupUrls(sgu)));
+ urlsPrimaryName.ifPresent(s -> props.put("urls.primaryName", s));
+ validatorUrl.ifPresent(s -> props.put("validatorUrl", s));
+ return props;
+ }
+
+ private void updateApiUrl(String openApiPath) {
+ if (!StringUtils.isEmpty(openApiPath)) {
+ final Optional openApiUri = Optional.of(URI.create(openApiPath));
+ if (!this.url.isPresent()) {
+ this.url = openApiUri;
+ }
+ }
+ }
+
+ private void updateAuthUrl() {
+ if (this.oauth.enable && !this.oauth2RedirectUrl.isPresent()) {
+ this.oauth2RedirectUrl = Optional.of(this.path + "/oauth2-redirect.html");
+ }
+ }
+
+ private void updateLayout() {
+ if (this.urls.isPresent() && this.layout.equals("BaseLayout")) {
+ this.layout = "StandaloneLayout";
+ }
+ }
+
+ private List getSupportedSubmitMethods(List supportedSubmitMethods) {
+ return supportedSubmitMethods.stream()
+ .filter(Objects::nonNull)
+ .map(Enum::name)
+ .map(String::toLowerCase)
+ .collect(Collectors.toList());
+ }
+
+ enum Expansion {
+ LIST,
+ FULL,
+ NONE
+ }
+
+ enum Rendering {
+ EXAMPLE,
+ MODEL
+ }
+
+ enum Sort {
+ ALPHA,
+ METHOD
+ }
+
+ enum Order {
+ ASC,
+ DESC;
+
+ public boolean isAscending() {
+ return this.equals(ASC);
+ }
+ }
+
+ private Set getSpecGroupUrls(Set specGroupUrls) {
+ Comparator specGroupUrlComparator;
+ if (groupsOrder.isAscending()) {
+ specGroupUrlComparator = Comparator.comparing(SpecGroupUrl::getName);
+ } else {
+ specGroupUrlComparator = (url1, url2) -> url2.getName().compareTo(url1.getName());
+ }
+
+ return specGroupUrls.stream()
+ .sorted(specGroupUrlComparator)
+ .filter(specGroupUrl -> StringUtils.isNotEmpty(specGroupUrl.getUrl().toString()))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+
+ }
+
+ public static class SpecGroupUrl {
+
+ URI url;
+ String name;
+
+ public SpecGroupUrl() {
+ }
+
+ public SpecGroupUrl(String specGroup) {
+ Objects.requireNonNull(specGroup, "specGroup cannot be null");
+ if (specGroup.contains("?group=")) {
+ String[] args = specGroup.trim().split("\\?group=");
+ if (args.length == 2) {
+ this.url = URI.create(args[0]);
+ this.name = args[1];
+ } else {
+ throw new IllegalArgumentException(
+ "Error, expected arguments number to be 2, but found: " + args.length +
+ ", " + specGroup + " must match the pattern: ?group=");
+ }
+ } else {
+ throw new IllegalArgumentException(specGroup + " doesn't match the pattern: ?group=");
+ }
+ }
+
+ public URI getUrl() {
+ return url;
+ }
+
+ public void setUrl(URI url) {
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ SpecGroupUrl that = (SpecGroupUrl) o;
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+
+ @Override
+ public String toString() {
+ return "SwaggerUrl{" + "url='" + url + '\'' +
+ ", name='" + name + '\'' +
+ '}';
+ }
+ }
+
+ /**
+ * Please @see Swagger UI OAuth 2.0
+ * documentation
+ */
+ @ConfigGroup
+ public static class OAuthConfig {
+
+ /**
+ * Indicates whether to setup OAuth 2.0
+ */
+ @ConfigItem
+ boolean enable;
+
+ /**
+ * Application name, displayed in authorization popup. MUST be a string.
+ */
+ @ConfigItem
+ Optional appName;
+
+ /**
+ * Default clientId. MUST be a string.
+ */
+ @ConfigItem
+ Optional clientId;
+
+ /**
+ * Never use this parameter in your production environment. It exposes cruicial security information.
+ * This feature is intended for dev/test environments only.
+ * Default clientSecret. MUST be a string
+ */
+ @ConfigItem
+ Optional clientSecret;
+
+ /**
+ * Realm query parameter (for oauth1) added to authorizationUrl and tokenUrl. MUST be a string.
+ */
+ @ConfigItem
+ Optional realm;
+
+ /**
+ * Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20).
+ * MUST be a string.
+ */
+ @ConfigItem
+ Optional scopeSeparator;
+
+ /**
+ * Additional query parameters added to authorizationUrl and tokenUrl. MUST be an object.
+ */
+ @ConfigItem
+ Map additionalQueryStringParams;
+
+ /**
+ * Only activated for the accessCode flow. During the authorization_code request to the tokenUrl,
+ * pass the Client Password using the HTTP Basic Authentication scheme
+ * (Authorization header with Basic base64encode(client_id + client_secret)). The default is false.
+ */
+ @ConfigItem
+ boolean useBasicAuthenticationWithAccessCodeGrant;
+
+ /**
+ * Only applies to authorizatonCode flows.
+ * Proof Key for Code Exchange brings enhanced security for OAuth public clients. The default is false.
+ */
+ @ConfigItem
+ boolean usePkceWithAuthorizationCodeGrant;
+
+ public Map getConfigParameters() {
+ final Map params = new TreeMap<>();
+ clientId.ifPresent(s -> params.put("clientId", s));
+ clientSecret.ifPresent(s -> params.put("clientSecret", s));
+ realm.ifPresent(s -> params.put("realm", s));
+ appName.ifPresent(s -> params.put("appName", s));
+ scopeSeparator.ifPresent(s -> params.put("scopeSeparator", s));
+ params.put("additionalQueryStringParams", additionalQueryStringParams);
+ params.put("useBasicAuthenticationWithAccessCodeGrant", useBasicAuthenticationWithAccessCodeGrant);
+ params.put("usePkceWithAuthorizationCodeGrant", usePkceWithAuthorizationCodeGrant);
+ return params;
+ }
+ }
+}
diff --git a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java
index 1b450ee4fbfb6..9e7adf95e50fa 100644
--- a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java
+++ b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java
@@ -10,6 +10,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
+import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
@@ -19,6 +20,14 @@
import org.jboss.logging.Logger;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.json.JsonWriteFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppDependency;
@@ -35,8 +44,6 @@
import io.quarkus.deployment.configuration.ConfigurationError;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.util.FileUtil;
-import io.quarkus.runtime.annotations.ConfigItem;
-import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.smallrye.openapi.common.deployment.SmallRyeOpenApiConfig;
import io.quarkus.swaggerui.runtime.SwaggerUiRecorder;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
@@ -53,16 +60,22 @@ public class SwaggerUiProcessor {
private static final String SWAGGER_UI_WEBJAR_ARTIFACT_ID = "swagger-ui";
private static final String SWAGGER_UI_WEBJAR_PREFIX = "META-INF/resources/webjars/swagger-ui";
private static final String SWAGGER_UI_FINAL_DESTINATION = "META-INF/swagger-ui-files";
- private static final Pattern SWAGGER_UI_DEFAULT_API_URL_PATTERN = Pattern.compile("(.* url: \")(.*)(\",.*)",
+ private static final Pattern SWAGGER_UI_CONFIG_PATTERN = Pattern.compile(
+ "(.*SwaggerUIBundle\\()(.*)(presets:.*)(layout:.*)(\\).*)",
Pattern.DOTALL);
private static final String TEMP_DIR_PREFIX = "quarkus-swagger-ui_" + System.nanoTime();
+ private static ObjectMapper objectMapper;
+
/**
* The configuration for Swagger UI.
*/
SwaggerUiConfig swaggerUiConfig;
- SmallRyeOpenApiConfig openapi;
+ /**
+ * The configuration for OpenAPI.
+ */
+ SmallRyeOpenApiConfig openApiConfig;
@Inject
private LaunchModeBuildItem launch;
@@ -95,7 +108,7 @@ public void registerSwaggerUiServletExtension(SwaggerUiRecorder recorder,
return;
}
- String openApiPath = httpRootPathBuildItem.adjustPath(openapi.path);
+ String openApiPath = httpRootPathBuildItem.adjustPath(openApiConfig.path);
if (launch.getLaunchMode().isDevOrTest()) {
CachedSwaggerUI cached = liveReloadBuildItem.getContextObject(CachedSwaggerUI.class);
@@ -118,7 +131,7 @@ public void registerSwaggerUiServletExtension(SwaggerUiRecorder recorder,
AppArtifact artifact = getSwaggerUiArtifact(curateOutcomeBuildItem);
Path tempDir = Files.createTempDirectory(TEMP_DIR_PREFIX).toRealPath();
extractSwaggerUi(artifact, tempDir);
- updateApiUrl(tempDir.resolve("index.html"), openApiPath);
+ updateSwaggerUiConfig(tempDir.resolve("index.html"), openApiPath);
cached.cachedDirectory = tempDir.toAbsolutePath().toString();
cached.cachedOpenAPIPath = openApiPath;
} catch (IOException e) {
@@ -147,8 +160,10 @@ public void registerSwaggerUiServletExtension(SwaggerUiRecorder recorder,
String filename = entry.getName().replace(versionedSwaggerUiWebjarPrefix, "");
byte[] content = FileUtil.readFileContents(inputStream);
if (entry.getName().endsWith("index.html")) {
- content = updateApiUrl(new String(content, StandardCharsets.UTF_8), openApiPath)
- .getBytes(StandardCharsets.UTF_8);
+ final String html = updateConfig(new String(content, StandardCharsets.UTF_8), openApiPath);
+ if (html != null) {
+ content = html.getBytes(StandardCharsets.UTF_8);
+ }
}
String fileName = SWAGGER_UI_FINAL_DESTINATION + "/" + filename;
generatedResources
@@ -198,47 +213,60 @@ private void extractSwaggerUi(AppArtifact artifact, Path resourceDir) throws IOE
}
}
- private void updateApiUrl(Path indexHtml, String openApiPath) throws IOException {
+ private void updateSwaggerUiConfig(Path indexHtml, String openApiPath) throws IOException {
String content = new String(Files.readAllBytes(indexHtml), StandardCharsets.UTF_8);
- String result = updateApiUrl(content, openApiPath);
+ String result = updateConfig(content, openApiPath);
if (result != null) {
Files.write(indexHtml, result.getBytes(StandardCharsets.UTF_8));
}
}
- public String updateApiUrl(String original, String openApiPath) {
-
- Matcher uriMatcher = SWAGGER_UI_DEFAULT_API_URL_PATTERN.matcher(original);
- if (uriMatcher.matches()) {
- return uriMatcher.replaceFirst("$1" + openApiPath + "$3");
+ private String updateConfig(String original, String openApiPath) throws JsonProcessingException {
+ Matcher configMatcher = SWAGGER_UI_CONFIG_PATTERN.matcher(original);
+ if (configMatcher.matches()) {
+ objectMapper = initObjectMapper();
+ String defaultConfig = configMatcher.group(3).trim();
+ String config;
+ String html;
+ config = objectMapper.writeValueAsString(swaggerUiConfig.getSwaggerUiProps(openApiPath));
+ html = configMatcher.replaceFirst("$1" + buildConfig(defaultConfig, config) + "$5");
+ if (swaggerUiConfig.oauth.enable) {
+ html = addOauthConfig(html, swaggerUiConfig.oauth.getConfigParameters());
+ }
+ return html;
} else {
- log.warn("Unable to replace the default URL of Swagger UI");
+ log.warn("Unable to replace the default configuration of Swagger UI");
return null;
}
}
- @ConfigRoot
- static final class SwaggerUiConfig {
- /**
- * The path where Swagger UI is available.
- *
- * The value `/` is not allowed as it blocks the application from serving anything else.
- */
- @ConfigItem(defaultValue = "/swagger-ui")
- String path;
+ private String buildConfig(String defaultConfig, String config) {
+ StringBuilder sb = new StringBuilder(config);
+ sb.insert(1, defaultConfig);
+ sb.insert(1, "\n");
+ return sb.toString();
+ }
- /**
- * If this should be included every time. By default this is only included when the application is running
- * in dev mode.
- */
- @ConfigItem
- boolean alwaysInclude;
+ private String addOauthConfig(String html, Map oauthParams) throws JsonProcessingException {
+ if (oauthParams.isEmpty()) {
+ return html;
+ }
+ StringBuilder sb = new StringBuilder("window.ui = ui\n");
+ sb.append("ui.initOAuth(\n");
+ String json = objectMapper.writeValueAsString(oauthParams);
+ sb.append(json);
+ sb.append("\n)");
+ return html.replace("window.ui = ui", sb.toString());
+ }
- /**
- * If Swagger UI should be enabled. By default, Swagger UI is enabled.
- */
- @ConfigItem(defaultValue = "true")
- boolean enable;
+ private static ObjectMapper initObjectMapper() {
+ return JsonMapper.builder()
+ .serializationInclusion(JsonInclude.Include.NON_NULL)
+ .serializationInclusion(JsonInclude.Include.NON_ABSENT)
+ .disable(JsonWriteFeature.QUOTE_FIELD_NAMES)
+ .enable(SerializationFeature.INDENT_OUTPUT)
+ .addModule(new Jdk8Module())
+ .build();
}
private static final class CachedSwaggerUI implements Runnable {
diff --git a/extensions/swagger-ui/deployment/src/main/resources/application-custom-config.properties b/extensions/swagger-ui/deployment/src/main/resources/application-custom-config.properties
new file mode 100644
index 0000000000000..3b19f38a2582b
--- /dev/null
+++ b/extensions/swagger-ui/deployment/src/main/resources/application-custom-config.properties
@@ -0,0 +1,33 @@
+quarkus.swagger-ui.enable=true
+quarkus.swagger-ui.config-url=/external
+quarkus.swagger-ui.deep-linking=true
+quarkus.swagger-ui.default-model-expand-depth=-1
+quarkus.swagger-ui.default-model-rendering=MODEL
+quarkus.swagger-ui.default-models-expand-depth=-1
+quarkus.swagger-ui.display-operation-id=true
+quarkus.swagger-ui.display-request-duration=true
+quarkus.swagger-ui.doc-expansion=NONE
+quarkus.swagger-ui.filter=true
+quarkus.swagger-ui.groups-order=DESC
+quarkus.swagger-ui.layout=BaseLayout
+quarkus.swagger-ui.max-displayed-tags=5
+quarkus.swagger-ui.oauth2-redirect-url=/custom/oauth2-redirect.html
+quarkus.swagger-ui.operations-sorter=METHOD
+quarkus.swagger-ui.path=/custom
+quarkus.swagger-ui.show-common-extensions=true
+quarkus.swagger-ui.show-extensions=true
+quarkus.swagger-ui.supported-submit-methods=OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, PATCH, OTHER
+quarkus.swagger-ui.tags-sorter=ALPHA
+quarkus.swagger-ui.urls-primary-name=/openapi?group=test
+quarkus.swagger-ui.urls=/openapi?group=test, /openapi?group=lahzouz
+quarkus.swagger-ui.validator-url=https://validator.swagger.io/validator
+
+quarkus.swagger-ui.oauth.enable=true
+quarkus.swagger-ui.oauth.app-name=quarkus
+quarkus.swagger-ui.oauth.client-id=your-client-id
+quarkus.swagger-ui.oauth.client-secret=your-client-secret-if-required
+quarkus.swagger-ui.oauth.realm=your-realms
+quarkus.swagger-ui.oauth.scope-separator=########
+quarkus.swagger-ui.oauth.use-basic-authentication-with-access-code-grant=true
+quarkus.swagger-ui.oauth.use-pkce-with-authorization-code-grant=true
+quarkus.swagger-ui.oauth.additional-query-string-params.test=quarkus
diff --git a/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/CustomConfigTest.java b/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/CustomConfigTest.java
index 7f29285cbddf0..e015a0e460986 100644
--- a/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/CustomConfigTest.java
+++ b/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/CustomConfigTest.java
@@ -1,9 +1,8 @@
package io.quarkus.swaggerui.deployment;
-import static org.hamcrest.Matchers.containsString;
-
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
-import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -15,12 +14,57 @@ public class CustomConfigTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withConfigurationResource("application-custom-config.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
- .addAsResource(new StringAsset("quarkus.swagger-ui.path=/custom"), "application.properties"));
+ .addAsManifestResource("application.properties", "microprofile-config.properties"));
@Test
public void shouldUseCustomConfig() {
- RestAssured.when().get("/custom").then().statusCode(200).body(containsString("/openapi"));
- RestAssured.when().get("/custom/index.html").then().statusCode(200).body(containsString("/openapi"));
+ final Matcher stringContainsInOrder = Matchers.stringContainsInOrder(
+ "const ui = SwaggerUIBundle(",
+ "deepLinking : true",
+ "defaultModelExpandDepth : -1",
+ "defaultModelRendering : \"model\"",
+ "defaultModelsExpandDepth : -1",
+ "displayOperationId : true",
+ "displayRequestDuration : true",
+ "docExpansion : \"none\"",
+ "dom_id : \"#swagger-ui\"",
+ "filter : true",
+ "layout : \"StandaloneLayout\"",
+ "maxDisplayedTags : 5",
+ "oauth2RedirectUrl : \"/custom/oauth2-redirect.html\"",
+ "operationsSorter : \"method\"",
+ "showCommonExtensions : true",
+ "showExtensions : true",
+ "supportedSubmitMethods : [ \"options\", \"get\", \"head\", \"post\", \"put\", \"delete\", \"trace\", \"connect\", \"patch\", \"other\" ]",
+ "tagsSorter : \"alpha\"",
+ "url : \"/openapi\"",
+ "urls : [ {\n" +
+ " url : \"/openapi\",\n" +
+ " name : \"test\"\n" +
+ " }, {\n" +
+ " url : \"/openapi\",\n" +
+ " name : \"lahzouz\"\n" +
+ " } ],",
+ "urls.primaryName : \"/openapi?group=test\"",
+ "validatorUrl : \"https://validator.swagger.io/validator\"");
+
+ RestAssured.when().get("/custom").then().statusCode(200).body(stringContainsInOrder);
+ RestAssured.when().get("/custom/index.html").then().statusCode(200).body(stringContainsInOrder);
+ }
+
+ @Test
+ public void shouldUseOauthCustomConfig() {
+ RestAssured.when().get("/custom/index.html").then().statusCode(200)
+ .body(Matchers.stringContainsInOrder(
+ "appName : \"quarkus\"",
+ "clientId : \"your-client-id\"",
+ "clientSecret : \"your-client-secret-if-required\"",
+ "realm : \"your-realms\"",
+ "scopeSeparator : \"########\"",
+ "useBasicAuthenticationWithAccessCodeGrant : true",
+ "usePkceWithAuthorizationCodeGrant : true"));
+ RestAssured.when().get("/custom/oauth2-redirect.html").then().statusCode(200);
}
}
diff --git a/extensions/swagger-ui/runtime/pom.xml b/extensions/swagger-ui/runtime/pom.xml
index dcad2198150d2..60ee5e2210d18 100644
--- a/extensions/swagger-ui/runtime/pom.xml
+++ b/extensions/swagger-ui/runtime/pom.xml
@@ -26,6 +26,10 @@
io.quarkus
quarkus-arc
+