Skip to content

Commit 257cd2f

Browse files
committed
Add scalar scalar-webmvc and scalar-webflux support. Fixes #3187
1 parent 4cd4717 commit 257cd2f

52 files changed

Lines changed: 180 additions & 273 deletions

File tree

Some content is hidden

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

pom.xml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
<spring-cloud-function.version>4.2.2</spring-cloud-function.version>
6464
<spring-security-oauth2-authorization-server.version>1.4.3
6565
</spring-security-oauth2-authorization-server.version>
66-
<scalar.version>0.4.3</scalar.version>
66+
<scalar.version>0.5.8</scalar.version>
6767
<skipPublishing>false</skipPublishing>
6868
<commons-lang3.version>3.18.0</commons-lang3.version>
6969
</properties>
@@ -103,7 +103,17 @@
103103
</dependency>
104104
<dependency>
105105
<groupId>com.scalar.maven</groupId>
106-
<artifactId>scalar</artifactId>
106+
<artifactId>scalar-core</artifactId>
107+
<version>${scalar.version}</version>
108+
</dependency>
109+
<dependency>
110+
<groupId>com.scalar.maven</groupId>
111+
<artifactId>scalar-webmvc</artifactId>
112+
<version>${scalar.version}</version>
113+
</dependency>
114+
<dependency>
115+
<groupId>com.scalar.maven</groupId>
116+
<artifactId>scalar-webflux</artifactId>
107117
<version>${scalar.version}</version>
108118
</dependency>
109119
<dependency>

springdoc-openapi-starter-common/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
</dependency>
9595
<dependency>
9696
<groupId>com.scalar.maven</groupId>
97-
<artifactId>scalar</artifactId>
97+
<artifactId>scalar-core</artifactId>
9898
<optional>true</optional>
9999
</dependency>
100100
</dependencies>

springdoc-openapi-starter-common/src/main/java/org/springdoc/scalar/AbstractScalarController.java

Lines changed: 51 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,23 @@
2727
package org.springdoc.scalar;
2828

2929
import java.io.IOException;
30-
import java.io.InputStream;
31-
import java.nio.charset.StandardCharsets;
3230

33-
import com.fasterxml.jackson.core.JsonProcessingException;
34-
import com.fasterxml.jackson.databind.ObjectMapper;
35-
import com.scalar.maven.webjar.ScalarProperties;
36-
import com.scalar.maven.webjar.internal.ScalarConfiguration;
37-
import com.scalar.maven.webjar.internal.ScalarConfigurationMapper;
31+
import com.scalar.maven.core.ScalarHtmlRenderer;
32+
import com.scalar.maven.core.ScalarProperties;
33+
import io.swagger.v3.oas.annotations.Operation;
3834

3935
import org.springframework.http.MediaType;
4036
import org.springframework.http.ResponseEntity;
37+
import org.springframework.web.bind.annotation.GetMapping;
4138

42-
import static org.springdoc.scalar.ScalarConstants.HTML_TEMPLATE_PATH;
4339
import static org.springdoc.scalar.ScalarConstants.SCALAR_DEFAULT_URL;
4440
import static org.springdoc.scalar.ScalarConstants.SCALAR_JS_FILENAME;
4541
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
4642

4743
/**
4844
* The type Abstract scalar controller.
4945
*
50-
* @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.
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.
5147
*/
5248
public abstract class AbstractScalarController {
5349

@@ -61,100 +57,73 @@ public abstract class AbstractScalarController {
6157
*/
6258
protected final String originalScalarUrl;
6359

64-
/**
65-
* The Object mapper.
66-
*/
67-
private final ObjectMapper objectMapper;
68-
6960
/**
7061
* Instantiates a new Abstract scalar controller.
7162
*
7263
* @param scalarProperties the scalar properties
73-
* @param objectMapper the object mapper
7464
*/
75-
protected AbstractScalarController(ScalarProperties scalarProperties, ObjectMapper objectMapper) {
65+
protected AbstractScalarController(ScalarProperties scalarProperties) {
7666
this.scalarProperties = scalarProperties;
7767
this.originalScalarUrl = scalarProperties.getUrl();
78-
this.objectMapper = objectMapper;
7968
}
8069

8170
/**
82-
* Serves the main API reference interface.
83-
* <p>This endpoint returns an HTML page that displays the Scalar API Reference
84-
* interface. The page is configured with the OpenAPI specification URL from
85-
* the properties.</p>
71+
* Gets scalar js.
8672
*
87-
* @param requestUrl the request url
88-
* @return a ResponseEntity containing the HTML content for the API reference interface
89-
* @throws IOException if the HTML template cannot be loaded
73+
* @return the scalar js
74+
* @throws IOException the io exception
9075
*/
91-
protected ResponseEntity<String> getDocs(String requestUrl) throws IOException {
92-
// Load the template HTML
93-
InputStream inputStream = getClass().getResourceAsStream(HTML_TEMPLATE_PATH);
94-
if (inputStream == null) {
95-
throw new IOException("HTML template not found at: " + HTML_TEMPLATE_PATH);
96-
}
97-
98-
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
99-
100-
// Replace the placeholders with actual values
101-
String bundleUrl = buildJsBundleUrl(requestUrl);
102-
String injectedHtml = html
103-
.replace("__JS_BUNDLE_URL__", bundleUrl)
104-
.replace("__CONFIGURATION__", buildConfigurationJson(buildApiDocsUrl(requestUrl)));
105-
76+
@GetMapping({ DEFAULT_PATH_SEPARATOR + SCALAR_JS_FILENAME, SCALAR_JS_FILENAME })
77+
@Operation(hidden = true)
78+
public ResponseEntity<byte[]> getScalarJs() throws IOException {
79+
byte[] jsContent = ScalarHtmlRenderer.getScalarJsContent();
10680
return ResponseEntity.ok()
107-
.contentType(MediaType.TEXT_HTML)
108-
.body(injectedHtml);
81+
.contentType(MediaType.valueOf("application/javascript"))
82+
.body(jsContent);
10983
}
11084

11185
/**
112-
* Serves the JavaScript bundle for the Scalar API Reference.
113-
* <p>This endpoint returns the JavaScript file that powers the Scalar API Reference
114-
* interface. The file is served with the appropriate MIME type.</p>
86+
* Gets docs.
11587
*
116-
* @return a ResponseEntity containing the JavaScript bundle
117-
* @throws IOException if the JavaScript file cannot be loaded
88+
* @param requestUrl the request url
89+
* @param apiDocsPath the api docs path
90+
* @param scalarPath the scalar path
91+
* @return the docs
92+
* @throws IOException the io exception
11893
*/
119-
protected ResponseEntity<byte[]> getScalarJs() throws IOException {
120-
// Load the scalar.js file
121-
InputStream inputStream = getClass().getResourceAsStream("/META-INF/resources/webjars/scalar/" + SCALAR_JS_FILENAME);
122-
if (inputStream == null) {
123-
return ResponseEntity.notFound().build();
124-
}
125-
126-
byte[] jsContent = inputStream.readAllBytes();
127-
94+
protected ResponseEntity<String> getDocs(String requestUrl, String apiDocsPath, String scalarPath) throws IOException {
95+
ScalarProperties configuredProperties = configureProperties(scalarProperties, requestUrl, apiDocsPath);
96+
String html = ScalarHtmlRenderer.render(configuredProperties);
97+
String bundleUrl = buildJsBundleUrl(requestUrl, scalarPath);
98+
html = html.replaceAll("(<script[^>]*\\s+src\\s*=\\s*\")([^\"]*)(\")", "$1"+bundleUrl+"$3");
12899
return ResponseEntity.ok()
129-
.contentType(MediaType.valueOf("application/javascript"))
130-
.body(jsContent);
100+
.contentType(MediaType.TEXT_HTML)
101+
.body(html);
131102
}
132103

133104
/**
134-
* Gets api docs url.
105+
* Configure properties scalar properties.
135106
*
136-
* @param requestUrl the request url
137-
* @param apiDocsPath the api docs path
138-
* @return the api docs url
107+
* @param properties the properties
108+
* @param requestUrl the request url
109+
* @param apiDocsPath the api docs path
110+
* @return the scalar properties
139111
*/
140-
protected String buildApiDocsUrl(String requestUrl, String apiDocsPath) {
141-
String apiDocsUrl = scalarProperties.getUrl();
142-
if (SCALAR_DEFAULT_URL.equals(originalScalarUrl)) {
143-
String serverUrl = requestUrl.substring(0, requestUrl.length() - scalarProperties.getPath().length());
144-
apiDocsUrl = serverUrl + apiDocsPath;
145-
}
146-
return apiDocsUrl;
112+
private ScalarProperties configureProperties(ScalarProperties properties, String requestUrl, String apiDocsPath ) {
113+
String url = buildApiDocsUrl(requestUrl, apiDocsPath);
114+
properties.setUrl(url);
115+
return properties;
147116
}
148117

149118
/**
150119
* Build js bundle url string.
151120
*
152-
* @param requestUrl the request url
153-
* @param scalarPath the scalar path
154-
* @return the string
121+
* @param requestUrl the request url
122+
* @param scalarPath the scalar path
123+
* @return the string
155124
*/
156-
protected String buildJsBundleUrl(String requestUrl, String scalarPath) {
157-
if (SCALAR_DEFAULT_URL.equals(originalScalarUrl)) {
125+
private String buildJsBundleUrl(String requestUrl, String scalarPath) {
126+
if (SCALAR_DEFAULT_URL.equals(originalScalarUrl) && requestUrl.contains("://")) {
158127
int firstPathSlash = requestUrl.indexOf('/', requestUrl.indexOf("://") + 3);
159128
String path = firstPathSlash >= 0 ? requestUrl.substring(firstPathSlash) : "/";
160129
if (path.endsWith("/"))
@@ -167,34 +136,16 @@ protected String buildJsBundleUrl(String requestUrl, String scalarPath) {
167136
/**
168137
* Gets api docs url.
169138
*
170-
* @param requestUrl the request url
171-
* @return the api docs url
172-
*/
173-
protected abstract String buildApiDocsUrl(String requestUrl);
174-
175-
/**
176-
* Build js bundle url string.
177-
*
178-
* @param requestUrl the request url
179-
* @return the string
180-
*/
181-
protected abstract String buildJsBundleUrl(String requestUrl);
182-
183-
/**
184-
* Build configuration json string.
185-
*
186-
* @param requestUrl the request url
187-
* @return the string
139+
* @param requestUrl the request url
140+
* @param apiDocsPath the api docs path
141+
* @return the api docs url
188142
*/
189-
private String buildConfigurationJson(String requestUrl) {
190-
try {
191-
this.scalarProperties.setUrl(requestUrl);
192-
ScalarConfiguration config = ScalarConfigurationMapper.map(scalarProperties);
193-
return objectMapper.writeValueAsString(config);
194-
}
195-
catch (JsonProcessingException e) {
196-
throw new RuntimeException("Failed to serialize Scalar configuration", e);
143+
private 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;
197148
}
149+
return apiDocsUrl;
198150
}
199-
200151
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/scalar/ScalarDisableAutoConfiguration.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,34 @@
3030
* @author bnasslahsen
3131
*/
3232

33+
import java.util.Set;
34+
3335
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
3436
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
3537

3638
/**
3739
* Disable Scalar WebJar AutoConfiguration.
3840
*/
39-
public class ScalarDisableAutoConfiguration implements AutoConfigurationImportFilter {
41+
public class ScalarDisableAutoConfiguration
42+
implements AutoConfigurationImportFilter {
4043

41-
/**
42-
* The constant TARGET.
43-
*/
44-
private static final String TARGET = "com.scalar.maven.webjar.ScalarAutoConfiguration";
44+
private static final Set<String> EXCLUDED = Set.of(
45+
"com.scalar.maven.webmvc.ScalarWebMvcAutoConfiguration",
46+
"com.scalar.maven.webflux.ScalarWebFluxAutoConfiguration"
47+
);
4548

4649
@Override
47-
public boolean[] match(String[] candidates, AutoConfigurationMetadata metadata) {
50+
public boolean[] match(
51+
String[] candidates,
52+
AutoConfigurationMetadata metadata) {
53+
4854
boolean[] matches = new boolean[candidates.length];
55+
4956
for (int i = 0; i < candidates.length; i++) {
5057
String candidate = candidates[i];
51-
// keep everything except the target
52-
matches[i] = !TARGET.equals(candidate);
58+
matches[i] = (candidate == null) || !EXCLUDED.contains(candidate);
5359
}
60+
5461
return matches;
5562
}
56-
}
63+
}

springdoc-openapi-starter-webflux-scalar/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</dependency>
1616
<dependency>
1717
<groupId>com.scalar.maven</groupId>
18-
<artifactId>scalar</artifactId>
18+
<artifactId>scalar-webflux</artifactId>
1919
</dependency>
2020
<!-- Actuator dependencies -->
2121
<dependency>

springdoc-openapi-starter-webflux-scalar/src/main/java/org/springdoc/webflux/scalar/ScalarActuatorController.java

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828

2929
import java.io.IOException;
3030

31-
import com.fasterxml.jackson.databind.ObjectMapper;
32-
import com.scalar.maven.webjar.ScalarProperties;
31+
import com.scalar.maven.webflux.SpringBootScalarProperties;
3332
import io.swagger.v3.oas.annotations.Operation;
3433
import org.springdoc.scalar.AbstractScalarController;
3534

@@ -41,7 +40,6 @@
4140

4241
import static org.springdoc.core.utils.Constants.DEFAULT_API_DOCS_ACTUATOR_URL;
4342
import static org.springdoc.scalar.ScalarConstants.DEFAULT_SCALAR_ACTUATOR_PATH;
44-
import static org.springdoc.scalar.ScalarConstants.SCALAR_JS_FILENAME;
4543
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
4644

4745
/**
@@ -60,31 +58,19 @@ public class ScalarActuatorController extends AbstractScalarController {
6058
*
6159
* @param scalarProperties the scalar properties
6260
* @param webEndpointProperties the web endpoint properties
63-
* @param objectMapper the object mapper
6461
*/
65-
protected ScalarActuatorController(ScalarProperties scalarProperties, WebEndpointProperties webEndpointProperties, ObjectMapper objectMapper) {
66-
super(scalarProperties, objectMapper);
62+
public ScalarActuatorController(SpringBootScalarProperties scalarProperties, WebEndpointProperties webEndpointProperties) {
63+
super(scalarProperties);
6764
this.webEndpointProperties = webEndpointProperties;
6865
}
6966

7067
@Operation(hidden = true)
7168
@GetMapping(DEFAULT_PATH_SEPARATOR)
7269
public ResponseEntity<String> getDocs(ServerHttpRequest serverHttpRequest) throws IOException {
73-
return super.getDocs(serverHttpRequest.getURI().toString());
74-
}
75-
76-
@Operation(hidden = true)
77-
@GetMapping(DEFAULT_PATH_SEPARATOR + SCALAR_JS_FILENAME)
78-
public ResponseEntity<byte[]> getScalarJs() throws IOException {
79-
return super.getScalarJs();
80-
}
81-
82-
public String buildApiDocsUrl(String requestUrl) {
83-
return buildApiDocsUrl(requestUrl, DEFAULT_PATH_SEPARATOR + DEFAULT_API_DOCS_ACTUATOR_URL);
84-
}
85-
86-
public String buildJsBundleUrl(String requestUrl) {
70+
String apiDocsPath = DEFAULT_PATH_SEPARATOR + DEFAULT_API_DOCS_ACTUATOR_URL;
71+
String requestUrl = serverHttpRequest.getURI().toString();
8772
String scalarPath = webEndpointProperties.getBasePath() + scalarProperties.getPath();
88-
return buildJsBundleUrl(requestUrl, scalarPath);
73+
return getDocs(requestUrl, apiDocsPath, scalarPath);
8974
}
75+
9076
}

0 commit comments

Comments
 (0)