Skip to content

Commit

Permalink
UI scripts now loading dynamiccaly to support contextPath from cookie.
Browse files Browse the repository at this point in the history
Fixed #1624

Swagger UI 5.17.14
OpenApi Explorer 2.2.720
Redoc 2.1.5
  • Loading branch information
altro3 committed Jul 15, 2024
1 parent b7a1048 commit 52009e8
Show file tree
Hide file tree
Showing 15 changed files with 769 additions and 389 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
projectVersion=6.11.1-SNAPSHOT
projectVersion=6.12.0-SNAPSHOT
projectGroup=io.micronaut.openapi

title=OpenAPI/Swagger Support
Expand Down
35 changes: 17 additions & 18 deletions openapi/src/main/java/io/micronaut/openapi/view/RapiPDFConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@
*/
package io.micronaut.openapi.view;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.openapi.view.OpenApiViewConfig.RendererType;
import io.micronaut.openapi.visitor.Pair;
import io.micronaut.openapi.visitor.group.OpenApiInfo;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

import static io.micronaut.openapi.view.OpenApiViewConfig.replacePlaceHolder;
import static io.micronaut.openapi.visitor.StringUtil.SLASH;

/**
Expand All @@ -44,7 +45,7 @@ final class RapiPDFConfig extends AbstractViewConfig {
DEFAULT_RAPIPDF_JS_PATH + "rapipdf-min.js"
);

private static final String LINK = "<script src='{{rapipdf.js.url.prefix}}rapipdf-min.js'></script>";
private static final String SCRIPT_RAPIPDF = "script(contextPath + \"{{rapipdf.js.url.prefix}}rapipdf-min.js\", head)";
private static final String TAG = "<rapi-pdf id='rapi-pdf' {{rapipdf.attributes}}></rapi-pdf>";
private static final String SPEC = "document.getElementById('rapi-pdf').setAttribute('spec-url', contextPath + '{{specURL}}');";
private static final Map<String, Object> DEFAULT_OPTIONS = new HashMap<>(6);
Expand Down Expand Up @@ -112,7 +113,6 @@ public boolean isEnabled() {
* @param properties A set of properties.
* @param openApiInfos Open API info objects.
* @param context Visitor context.
*
* @return A RapipdfConfig.
*/
static RapiPDFConfig fromProperties(Map<String, String> properties, Map<Pair<String, String>, OpenApiInfo> openApiInfos, VisitorContext context) {
Expand All @@ -127,7 +127,6 @@ static RapiPDFConfig fromProperties(Map<String, String> properties, Map<Pair<Str
* @param template A template.
* @param rendererType The renderer type.
* @param context Visitor context.
*
* @return The template with placeholders replaced.
*/
String render(String template, RendererType rendererType, VisitorContext context) {
Expand All @@ -145,18 +144,18 @@ String render(String template, RendererType rendererType, VisitorContext context
options.put("style", DEFAULT_RAPIDOC_STYLE);
}
}
String script = OpenApiViewConfig.replacePlaceHolder(LINK, "rapipdf.js.url.prefix", isDefaultJsUrl ? getFinalUrlPrefix(rendererType, context) : jsUrl, StringUtils.EMPTY_STRING);
String rapipdfTag = OpenApiViewConfig.replacePlaceHolder(TAG, "rapipdf.attributes", toHtmlAttributes(), StringUtils.EMPTY_STRING);
String script = replacePlaceHolder(SCRIPT_RAPIPDF, "rapipdf.js.url.prefix", isDefaultJsUrl ? getFinalUrlPrefix(rendererType, context) : jsUrl, StringUtils.EMPTY_STRING);
String rapipdfTag = replacePlaceHolder(TAG, "rapipdf.attributes", toHtmlAttributes(), StringUtils.EMPTY_STRING);
if (styleUpdated) {
options.remove("style");
}
template = OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.script", script, StringUtils.EMPTY_STRING);
template = OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.specurl", SPEC, StringUtils.EMPTY_STRING);
return OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.tag", rapipdfTag, StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, "rapipdf.script", script, StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, "rapipdf.specurl", SPEC, StringUtils.EMPTY_STRING);
return replacePlaceHolder(template, "rapipdf.tag", rapipdfTag, StringUtils.EMPTY_STRING);
} else {
template = OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.script", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
template = OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.specurl", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
return OpenApiViewConfig.replacePlaceHolder(template, "rapipdf.tag", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, "rapipdf.script", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, "rapipdf.specurl", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
return replacePlaceHolder(template, "rapipdf.tag", StringUtils.EMPTY_STRING, StringUtils.EMPTY_STRING);
}
}

Expand Down
16 changes: 8 additions & 8 deletions openapi/src/main/java/io/micronaut/openapi/view/RedocConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
*/
package io.micronaut.openapi.view;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.openapi.view.OpenApiViewConfig.RendererType;
import io.micronaut.openapi.visitor.Pair;
import io.micronaut.openapi.visitor.group.OpenApiInfo;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static io.micronaut.openapi.visitor.StringUtil.SLASH;

/**
Expand All @@ -40,7 +40,8 @@ final class RedocConfig extends AbstractViewConfig {
public static final String REDOC_PREFIX = "redoc.";
private static final String DEFAULT_REDOC_JS_PATH = OpenApiViewConfig.RESOURCE_DIR + SLASH;

private static final List<String> RESOURCE_FILES = Collections.singletonList(
private static final List<String> RESOURCE_FILES = List.of(
DEFAULT_REDOC_JS_PATH + "font.css",
DEFAULT_REDOC_JS_PATH + "redoc.standalone.js"
);

Expand Down Expand Up @@ -144,7 +145,6 @@ private RedocConfig(Map<Pair<String, String>, OpenApiInfo> openApiInfos) {
* @param properties A set of properties.
* @param openApiInfos Open API info objects.
* @param context Visitor context.
*
* @return A RedocConfig.
*/
static RedocConfig fromProperties(Map<String, String> properties, Map<Pair<String, String>, OpenApiInfo> openApiInfos, VisitorContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@
*/
package io.micronaut.openapi.view;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
Expand All @@ -32,6 +24,15 @@
import io.micronaut.openapi.visitor.Pair;
import io.micronaut.openapi.visitor.group.OpenApiInfo;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static io.micronaut.openapi.view.OpenApiViewConfig.replacePlaceHolder;
import static io.micronaut.openapi.visitor.StringUtil.DOT;
import static io.micronaut.openapi.visitor.StringUtil.SLASH;

Expand All @@ -45,6 +46,7 @@ final class SwaggerUIConfig extends AbstractViewConfig {
private static final String DEFAULT_SWAGGER_JS_PATH = OpenApiViewConfig.RESOURCE_DIR + SLASH;

private static final List<String> RESOURCE_FILES = List.of(
DEFAULT_SWAGGER_JS_PATH + "index.css",
DEFAULT_SWAGGER_JS_PATH + "swagger-ui.css",
DEFAULT_SWAGGER_JS_PATH + "favicon-16x16.png",
DEFAULT_SWAGGER_JS_PATH + "favicon-32x32.png",
Expand Down Expand Up @@ -238,7 +240,6 @@ static boolean hasOauth2Option(Map<String, Object> options) {
* @param properties A set of properties.
* @param openApiInfos Open API info objects.
* @param context Visitor context.
*
* @return A SwaggerUIConfig.
*/
static SwaggerUIConfig fromProperties(Map<String, String> properties, Map<Pair<String, String>, OpenApiInfo> openApiInfos, VisitorContext context) {
Expand All @@ -265,10 +266,10 @@ public String render(String template, @Nullable VisitorContext context) {
String finalUrlPrefix = getFinalUrlPrefix(RendererType.SWAGGER_UI, context);

template = rapiPDFConfig.render(template, RendererType.SWAGGER_UI, context);
template = OpenApiViewConfig.replacePlaceHolder(template, PREFIX_SWAGGER_UI + ".js.url.prefix", isDefaultJsUrl ? finalUrlPrefix : jsUrl, StringUtils.EMPTY_STRING);
template = OpenApiViewConfig.replacePlaceHolder(template, PREFIX_SWAGGER_UI + ".attributes", toOptions(), StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, PREFIX_SWAGGER_UI + ".js.url.prefix", isDefaultJsUrl ? finalUrlPrefix : jsUrl, StringUtils.EMPTY_STRING);
template = replacePlaceHolder(template, PREFIX_SWAGGER_UI + ".attributes", toOptions(), StringUtils.EMPTY_STRING);
template = template.replace("{{" + PREFIX_SWAGGER_UI + ".theme}}", theme == null || Theme.CLASSIC == theme ? StringUtils.EMPTY_STRING :
"<link rel='stylesheet' type='text/css' href='" + (isDefaultThemeUrl ? finalUrlPrefix + theme.getCss() + ".css" : themeUrl) + "' />");
"link(contextPath + \"" + (isDefaultThemeUrl ? finalUrlPrefix + theme.getCss() + ".css" : themeUrl) + "\", head, \"text/css\", \"stylesheet\")");
template = template.replace("{{" + PREFIX_SWAGGER_UI + DOT + OPTION_OAUTH2 + "}}", hasOauth2Option(options) ? toOauth2Options() : StringUtils.EMPTY_STRING);
template = template.replace("{{" + PREFIX_SWAGGER_UI + DOT + OPTION_PRIMARY_NAME + "}}", StringUtils.isNotEmpty(primaryName) ? getPrimaryName(context) : StringUtils.EMPTY_STRING);
template = template.replace("{{" + PREFIX_SWAGGER_UI + DOT + OPTION_URLS + "}}", getUrlStr(context));
Expand Down
101 changes: 64 additions & 37 deletions openapi/src/main/resources/templates/openapi-explorer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1,user-scalable=yes">
<title>OpenAPI Explorer</title>
<link rel="stylesheet" href="{{openapi-explorer.js.url.prefix}}default.min.css">
<link rel="stylesheet" href="{{openapi-explorer.js.url.prefix}}bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{{openapi-explorer.js.url.prefix}}font-awesome.min.css">
<script src="{{openapi-explorer.js.url.prefix}}openapi-explorer.min.js" type="module" defer></script>
<style>
html, body {
height: 100%
Expand Down Expand Up @@ -66,7 +62,6 @@
</head>
<body>


<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid w-100 d-flex justify-content-between align-items-center">
<div class="">
Expand Down Expand Up @@ -98,43 +93,75 @@
<!--collapse table schema-description-expanded="true" nav-item-spacing="compact" show-components="true" bg-color="#FFFFFF" header-bg-color="#DEE2E6" nav-bg-color="#1D2F3B" text-color="#465865" nav-hover-text-color="#FFFFFF" primary-color="#1D2F3B" secondary-color="#FBAF0B"-->
<script>
const extract = function(v) {
return decodeURIComponent(v.replace(/(?:(?:^|.*;\s*)contextPath\s*\=\s*([^;]*).*$)|^.*$/, "$1"));
},
cookie = extract(document.cookie),
contextPath = cookie === '' ? extract(window.location.search.substring(1)) : cookie,
openApiExplorer = document.getElementById('openapi-explorer');
let specUrl = '{{specURL}}';
if (contextPath !== '') {
specUrl = contextPath + '{{specURL}}';
return decodeURIComponent(v.replace(/(?:^|.*;\s*)contextPath\s*=\s*([^;]*).*$|^.*$/, "$1"));
}
const cookie = extract(document.cookie)
const contextPath = cookie === '' ? extract(window.location.search.substring(1)) : cookie
const openApiExplorer = document.getElementById('openapi-explorer');
const head = document.getElementsByTagName('head')[0]

link(contextPath + "{{openapi-explorer.js.url.prefix}}default.min.css", head, "text/css", "stylesheet")
link(contextPath + "{{openapi-explorer.js.url.prefix}}bootstrap.min.css", head, "text/css", "stylesheet", "anonymous")
link(contextPath + "{{openapi-explorer.js.url.prefix}}font-awesome.min.css", head, "text/css", "stylesheet")
const openapiExplorerJs = script(contextPath + "{{openapi-explorer.js.url.prefix}}openapi-explorer.min.js", head, "module", true)

openapiExplorerJs.onload = function () {
let specUrl = '{{specURL}}';
if (contextPath !== '') {
specUrl = contextPath + '{{specURL}}';
openApiExplorer.setAttribute('spec-url', specUrl);
openApiExplorer.addEventListener('spec-loaded', e => {
e.detail.tags.forEach(tag => tag.paths.forEach(path => path.path = contextPath + path.path));
openApiExplorer.requestUpdate();
});
}
openApiExplorer.setAttribute('spec-url', specUrl);
openApiExplorer.addEventListener('spec-loaded', e => {
e.detail.tags.forEach(tag => tag.paths.forEach(path => path.path = contextPath + path.path));
openApiExplorer.requestUpdate();
document.addEventListener('DOMContentLoaded', async () => {
const specUrl = document.getElementById('specUrl');
const currentSpecUrl = new URLSearchParams(window.location.search);
const newSpecUrl = currentSpecUrl.get('specUrl') || currentSpecUrl.get('url') || currentSpecUrl.get('spec');
if (newSpecUrl) {
document.getElementsByTagName("openapi-explorer")[0].setAttribute('spec-url', newSpecUrl);
specUrl.value = newSpecUrl;
}

const loadButton = document.getElementById('loadButton');
loadButton.addEventListener('click', () => {
try {
const newUrl = new URL(window.location);
newUrl.searchParams.set('url', specUrl.value);
window.location.assign(newUrl.toString());
document.getElementsByTagName("openapi-explorer")[0].setAttribute('spec-url', specUrl.value);
} catch (error) {
console.error('Failed to set spec url into url address bar', error);
}
});
});
}
openApiExplorer.setAttribute('spec-url', specUrl);
document.addEventListener('DOMContentLoaded', async () => {
const specUrl = document.getElementById('specUrl');
const currentSpecUrl = new URLSearchParams(window.location.search);
const newSpecUrl = currentSpecUrl.get('specUrl') || currentSpecUrl.get('url') || currentSpecUrl.get('spec');
if (newSpecUrl) {
document.getElementsByTagName("openapi-explorer")[0].setAttribute('spec-url', newSpecUrl);
specUrl.value = newSpecUrl;

function link(href, head, type, rel, crossorigin) {
const el = document.createElement('link');
el.href = href;
el.type = type;
el.rel = rel;
if (crossorigin !== undefined && crossorigin) {
el.crossorigin = crossorigin;
}
head.appendChild(el);
}

const loadButton = document.getElementById('loadButton');
loadButton.addEventListener('click', () => {
try {
const newUrl = new URL(window.location);
newUrl.searchParams.set('url', specUrl.value);
window.location.assign(newUrl.toString());
document.getElementsByTagName("openapi-explorer")[0].setAttribute('spec-url', specUrl.value);
} catch (error) {
console.error('Failed to set spec url into url address bar', error);
}
});
});
{{rapipdf.specurl}}
function script(src, head, type, defer) {
const el = document.createElement('script');
el.src = src;
if (type !== undefined && type) {
el.type = type;
}
if (defer !== undefined && defer) {
el.setAttribute("defer", "");
}
head.appendChild(el);
return el;
}
</script>

</body>
Expand Down

Large diffs are not rendered by default.

43 changes: 29 additions & 14 deletions openapi/src/main/resources/templates/rapidoc/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
<!-- Important: The Custom element uses utf8 characters -->
<meta charset='utf-8' />
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1,user-scalable=yes">
<script defer="defer" src='{{rapidoc.js.url.prefix}}rapidoc-min.js'></script>
{{rapipdf.script}}
</head>
<script>
window.addEventListener('DOMContentLoaded', () => {
Expand All @@ -21,19 +19,36 @@
<rapi-doc id='rapidoc' {{rapidoc.attributes}}>{{rapipdf.tag}}</rapi-doc>
<script>
const extract = function(v) {
return decodeURIComponent(v.replace(/(?:(?:^|.*;\s*)contextPath\s*\=\s*([^;]*).*$)|^.*$/, "$1"));
},
cookie = extract(document.cookie),
contextPath = cookie === '' ? extract(window.location.search.substring(1)) : cookie,
rapidoc = document.getElementById('rapidoc');
if (contextPath !== '') {
rapidoc.addEventListener('spec-loaded', e => {
e.detail.tags.forEach(tag => tag.paths.forEach(path => path.path = contextPath + path.path));
rapidoc.requestUpdate();
});
return decodeURIComponent(v.replace(/(?:^|.*;\s*)contextPath\s*=\s*([^;]*).*$|^.*$/, "$1"));
}
const cookie = extract(document.cookie)
const contextPath = cookie === '' ? extract(window.location.search.substring(1)) : cookie
const head = document.getElementsByTagName('head')[0];
{{rapipdf.script}}
const rapidocJs = script(contextPath + "{{rapidoc.js.url.prefix}}rapidoc-min.js", head, true)

rapidocJs.onload = function () {

const rapidoc = document.getElementById('rapidoc')
if (contextPath !== '') {
rapidoc.addEventListener('spec-loaded', e => {
e.detail.tags.forEach(tag => tag.paths.forEach(path => path.path = contextPath + path.path));
rapidoc.requestUpdate();
});
}
rapidoc.setAttribute('spec-url', contextPath + '{{specURL}}');
{{rapipdf.specurl}}
}

function script(src, head, type, defer) {
const el = document.createElement('script');
el.src = src;
if (defer !== undefined && defer) {
el.setAttribute("defer", "");
}
head.appendChild(el);
return el;
}
rapidoc.setAttribute('spec-url', contextPath + '{{specURL}}');
{{rapipdf.specurl}}
</script>
</body>
</html>
Loading

0 comments on commit 52009e8

Please sign in to comment.