Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e0feb04

Browse files
Adding initial Scalar Support. Fixes #3084
1 parent 31ba90b commit e0feb04

File tree

122 files changed

+5416
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+5416
-0
lines changed

‎pom.xml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
<module>springdoc-openapi-starter-webflux-api</module>
4545
<module>springdoc-openapi-starter-webmvc-ui</module>
4646
<module>springdoc-openapi-starter-webflux-ui</module>
47+
<module>springdoc-openapi-starter-webmvc-scalar</module>
48+
<module>springdoc-openapi-starter-webflux-scalar</module>
4749
<module>springdoc-openapi-bom</module>
4850
</modules>
4951

@@ -61,6 +63,7 @@
6163
<spring-cloud-function.version>4.2.2</spring-cloud-function.version>
6264
<spring-security-oauth2-authorization-server.version>1.4.3
6365
</spring-security-oauth2-authorization-server.version>
66+
<scalar.version>0.1.0</scalar.version>
6467
</properties>
6568

6669
<dependencyManagement>
@@ -96,6 +99,11 @@
9699
<artifactId>spring-security-oauth2-authorization-server</artifactId>
97100
<version>${spring-security-oauth2-authorization-server.version}</version>
98101
</dependency>
102+
<dependency>
103+
<groupId>com.scalar.maven</groupId>
104+
<artifactId>scalar</artifactId>
105+
<version>${scalar.version}</version>
106+
</dependency>
99107
</dependencies>
100108
</dependencyManagement>
101109
<dependencies>

‎springdoc-openapi-starter-common/pom.xml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@
9393
<artifactId>querydsl-core</artifactId>
9494
<optional>true</optional>
9595
</dependency>
96+
<dependency>
97+
<groupId>com.scalar.maven</groupId>
98+
<artifactId>scalar</artifactId>
99+
<optional>true</optional>
100+
</dependency>
96101
</dependencies>
97102
<build>
98103
<resources>
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package org.springdoc.scalar;
28+
29+
import java.io.IOException;
30+
import java.io.InputStream;
31+
import java.net.URLDecoder;
32+
import java.nio.charset.StandardCharsets;
33+
34+
import com.scalar.maven.webjar.ScalarProperties;
35+
36+
import org.springframework.http.MediaType;
37+
import org.springframework.http.ResponseEntity;
38+
39+
import static org.springdoc.scalar.ScalarConstants.SCALAR_DEFAULT_URL;
40+
import static org.springdoc.scalar.ScalarConstants.SCALAR_JS_FILENAME;
41+
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
42+
43+
/**
44+
* The type Abstract scalar controller.
45+
*
46+
* @author bnasslahsen This is a copy of the class <a href="com.scalar.maven.webjar.ScalarController">ScalarController</a> from the scalar webjar. It has been slightly modified to fit the springdoc-openapi code base.
47+
*/
48+
public abstract class AbstractScalarController {
49+
50+
/**
51+
* The Scalar properties.
52+
*/
53+
protected final ScalarProperties scalarProperties;
54+
55+
/**
56+
* The Original scalar url.
57+
*/
58+
protected final String originalScalarUrl;
59+
60+
/**
61+
* Instantiates a new Abstract scalar controller.
62+
*
63+
* @param scalarProperties the scalar properties
64+
*/
65+
protected AbstractScalarController(ScalarProperties scalarProperties) {
66+
this.scalarProperties = scalarProperties;
67+
this.originalScalarUrl = scalarProperties.getUrl();
68+
}
69+
70+
/**
71+
* Serves the main API reference interface.
72+
* <p>This endpoint returns an HTML page that displays the Scalar API Reference
73+
* interface. The page is configured with the OpenAPI specification URL from
74+
* the properties.</p>
75+
*
76+
* @param requestUrl the request url
77+
* @return a ResponseEntity containing the HTML content for the API reference interface
78+
* @throws IOException if the HTML template cannot be loaded
79+
*/
80+
protected ResponseEntity<String> getDocs(String requestUrl) throws IOException {
81+
// Load the template HTML
82+
InputStream inputStream = getClass().getResourceAsStream("/META-INF/resources/webjars/scalar/index.html");
83+
if (inputStream == null) {
84+
return ResponseEntity.notFound().build();
85+
}
86+
87+
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
88+
requestUrl = decode(requestUrl);
89+
// Replace the placeholders with actual values
90+
String cdnUrl = buildJsBundleUrl(requestUrl);
91+
String injectedHtml = html
92+
.replace("__JS_BUNDLE_URL__", cdnUrl)
93+
.replace("__CONFIGURATION__", """
94+
{
95+
url: "%s"
96+
}
97+
""".formatted(buildApiDocsUrl(requestUrl)));
98+
99+
return ResponseEntity.ok()
100+
.contentType(MediaType.TEXT_HTML)
101+
.body(injectedHtml);
102+
}
103+
104+
/**
105+
* Serves the JavaScript bundle for the Scalar API Reference.
106+
* <p>This endpoint returns the JavaScript file that powers the Scalar API Reference
107+
* interface. The file is served with the appropriate MIME type.</p>
108+
*
109+
* @return a ResponseEntity containing the JavaScript bundle
110+
* @throws IOException if the JavaScript file cannot be loaded
111+
*/
112+
protected ResponseEntity<byte[]> getScalarJs() throws IOException {
113+
// Load the scalar.js file
114+
InputStream inputStream = getClass().getResourceAsStream("/META-INF/resources/webjars/scalar/" + SCALAR_JS_FILENAME);
115+
if (inputStream == null) {
116+
return ResponseEntity.notFound().build();
117+
}
118+
119+
byte[] jsContent = inputStream.readAllBytes();
120+
121+
return ResponseEntity.ok()
122+
.contentType(MediaType.valueOf("application/javascript"))
123+
.body(jsContent);
124+
}
125+
126+
/**
127+
* Decode string.
128+
*
129+
* @param requestURI the request uri
130+
* @return the string
131+
*/
132+
protected String decode(String requestURI) {
133+
return URLDecoder.decode(requestURI, StandardCharsets.UTF_8);
134+
}
135+
136+
/**
137+
* Gets api docs url.
138+
*
139+
* @param requestUrl the request url
140+
* @param apiDocsPath the api docs path
141+
* @return the api docs url
142+
*/
143+
protected String buildApiDocsUrl(String requestUrl, String apiDocsPath) {
144+
String apiDocsUrl = scalarProperties.getUrl();
145+
if (SCALAR_DEFAULT_URL.equals(originalScalarUrl)) {
146+
String serverUrl = requestUrl.substring(0, requestUrl.length() - scalarProperties.getPath().length());
147+
apiDocsUrl = serverUrl + apiDocsPath;
148+
}
149+
return apiDocsUrl;
150+
}
151+
152+
/**
153+
* Build js bundle url string.
154+
*
155+
* @param requestUrl the request url
156+
* @param scalarPath the scalar path
157+
* @return the string
158+
*/
159+
protected String buildJsBundleUrl(String requestUrl, String scalarPath) {
160+
if (SCALAR_DEFAULT_URL.equals(originalScalarUrl)) {
161+
int firstPathSlash = requestUrl.indexOf('/', requestUrl.indexOf("://") + 3);
162+
String path = firstPathSlash >= 0 ? requestUrl.substring(firstPathSlash) : "/";
163+
if( path.endsWith("/"))
164+
path = path.substring(0, path.length() - 1);
165+
return path + DEFAULT_PATH_SEPARATOR + SCALAR_JS_FILENAME;
166+
}
167+
return scalarPath + DEFAULT_PATH_SEPARATOR + SCALAR_JS_FILENAME;
168+
}
169+
170+
/**
171+
* Gets api docs url.
172+
*
173+
* @param requestUrl the request url
174+
* @return the api docs url
175+
*/
176+
protected abstract String buildApiDocsUrl(String requestUrl);
177+
178+
/**
179+
* Build js bundle url string.
180+
*
181+
* @param requestUrl the request url
182+
* @return the string
183+
*/
184+
protected abstract String buildJsBundleUrl(String requestUrl);
185+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package org.springdoc.scalar;
28+
29+
/**
30+
* The type ScalarConstants.
31+
*
32+
* @author bnasslahsen
33+
*/
34+
public class ScalarConstants {
35+
36+
/**
37+
* The constant SCALAR_DEFAULT_PATH.
38+
*/
39+
public static final String SCALAR_DEFAULT_PATH = "/scalar";
40+
41+
/**
42+
* The constant SCALAR_JS_FILENAME.
43+
*/
44+
public static final String SCALAR_JS_FILENAME = "scalar.js";
45+
46+
/**
47+
* The constant SCALAR_DEFAULT_URL.
48+
*/
49+
public static final String SCALAR_DEFAULT_URL = "https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json";
50+
51+
/**
52+
* The constant DEFAULT_SCALAR_ACTUATOR_PATH.
53+
*/
54+
public static final String DEFAULT_SCALAR_ACTUATOR_PATH = "scalar";
55+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package org.springdoc.scalar;
28+
29+
/**
30+
* @author bnasslahsen
31+
*/
32+
33+
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
34+
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
35+
36+
/**
37+
* Disable Scalar WebJar AutoConfiguration.
38+
*/
39+
public class ScalarDisableAutoConfiguration implements AutoConfigurationImportFilter {
40+
41+
/**
42+
* The constant TARGET.
43+
*/
44+
private static final String TARGET = "com.scalar.maven.webjar.ScalarAutoConfiguration";
45+
46+
@Override
47+
public boolean[] match(String[] candidates, AutoConfigurationMetadata metadata) {
48+
boolean[] matches = new boolean[candidates.length];
49+
for (int i = 0; i < candidates.length; i++) {
50+
String candidate = candidates[i];
51+
// keep everything except the target
52+
matches[i] = !TARGET.equals(candidate);
53+
}
54+
return matches;
55+
}
56+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
2+
org.springdoc.scalar.ScalarDisableAutoConfiguration

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /