Skip to content

Commit 1541d70

Browse files
committed
OpenAPI: fix handling of paths given in configuration
Fixes #26870 Signed-off-by: Michael Edgar <[email protected]>
1 parent bbb56f9 commit 1541d70

File tree

4 files changed

+143
-21
lines changed

4 files changed

+143
-21
lines changed

extensions/smallrye-openapi/deployment/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
<groupId>io.smallrye</groupId>
6666
<artifactId>smallrye-open-api-vertx</artifactId>
6767
</dependency>
68+
69+
<!-- Test -->
6870
<dependency>
6971
<groupId>io.quarkus</groupId>
7072
<artifactId>quarkus-resteasy-deployment</artifactId>
@@ -85,6 +87,16 @@
8587
<artifactId>rest-assured</artifactId>
8688
<scope>test</scope>
8789
</dependency>
90+
<dependency>
91+
<groupId>org.mockito</groupId>
92+
<artifactId>mockito-core</artifactId>
93+
<scope>test</scope>
94+
</dependency>
95+
<dependency>
96+
<groupId>jakarta.ws.rs</groupId>
97+
<artifactId>jakarta.ws.rs-api</artifactId>
98+
<scope>test</scope>
99+
</dependency>
88100
</dependencies>
89101

90102
<build>
Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
package io.quarkus.smallrye.openapi.deployment;
22

3+
import java.util.Arrays;
34
import java.util.Collection;
5+
import java.util.Objects;
6+
import java.util.Optional;
7+
import java.util.Set;
8+
import java.util.TreeSet;
9+
import java.util.function.Predicate;
10+
import java.util.stream.Collectors;
11+
import java.util.stream.Stream;
412

13+
import org.jboss.jandex.AnnotationInstance;
14+
import org.jboss.jandex.AnnotationValue;
515
import org.jboss.jandex.ClassInfo;
16+
import org.jboss.jandex.DotName;
617

718
import io.smallrye.openapi.runtime.scanner.AnnotationScannerExtension;
819
import io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner;
@@ -12,14 +23,59 @@
1223
*/
1324
public class CustomPathExtension implements AnnotationScannerExtension {
1425

15-
private final String defaultPath;
26+
static final Set<DotName> APPLICATION_PATH = new TreeSet<>(Arrays.asList(
27+
DotName.createSimple("jakarta.ws.rs.ApplicationPath"),
28+
DotName.createSimple("javax.ws.rs.ApplicationPath")));
1629

17-
public CustomPathExtension(String defaultPath) {
18-
this.defaultPath = defaultPath;
30+
private final String rootPath;
31+
private final String appPath;
32+
33+
public CustomPathExtension(String rootPath, String appPath) {
34+
this.rootPath = rootPath;
35+
this.appPath = appPath;
1936
}
2037

2138
@Override
2239
public void processScannerApplications(AnnotationScanner scanner, Collection<ClassInfo> applications) {
23-
scanner.setContextRoot(defaultPath);
40+
Optional<String> appPathAnnotationValue = applications.stream()
41+
.flatMap(app -> APPLICATION_PATH.stream().map(app::declaredAnnotation))
42+
.filter(Objects::nonNull)
43+
.map(AnnotationInstance::value)
44+
.map(AnnotationValue::asString)
45+
.findFirst();
46+
47+
/*
48+
* If the @ApplicationPath was found, ignore the appPath given in configuration and only
49+
* use the rootPath for the contextRoot.
50+
*/
51+
String contextRoot = appPathAnnotationValue.map(path -> buildContextRpot(rootPath))
52+
.orElseGet(() -> buildContextRpot(rootPath, this.appPath));
53+
54+
if (!"/".equals(contextRoot)) {
55+
scanner.setContextRoot(contextRoot);
56+
}
57+
}
58+
59+
static String buildContextRpot(String... segments) {
60+
String path = Stream.of(segments)
61+
.filter(Objects::nonNull)
62+
.map(CustomPathExtension::stripSlashes)
63+
.filter(Predicate.not(String::isEmpty))
64+
.map("/"::concat)
65+
.collect(Collectors.joining());
66+
67+
return path.isEmpty() ? "/" : path;
68+
}
69+
70+
static String stripSlashes(String segment) {
71+
if (segment.startsWith("/")) {
72+
segment = segment.substring(1);
73+
}
74+
75+
if (segment.endsWith("/")) {
76+
segment = segment.substring(0, segment.length() - 1);
77+
}
78+
79+
return segment;
2480
}
2581
}

extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.HashSet;
2424
import java.util.List;
2525
import java.util.Map;
26-
import java.util.Optional;
2726
import java.util.Set;
2827
import java.util.function.Consumer;
2928
import java.util.regex.Matcher;
@@ -79,7 +78,6 @@
7978
import io.quarkus.deployment.util.IoUtil;
8079
import io.quarkus.resteasy.common.spi.ResteasyDotNames;
8180
import io.quarkus.resteasy.server.common.spi.AllowedJaxRsAnnotationPrefixBuildItem;
82-
import io.quarkus.resteasy.server.common.spi.ResteasyJaxrsConfigBuildItem;
8381
import io.quarkus.runtime.LaunchMode;
8482
import io.quarkus.runtime.util.ClassPathUtils;
8583
import io.quarkus.security.Authenticated;
@@ -689,7 +687,6 @@ public void build(BuildProducer<FeatureBuildItem> feature,
689687
HttpRootPathBuildItem httpRootPathBuildItem,
690688
OutputTargetBuildItem out,
691689
SmallRyeOpenApiConfig smallRyeOpenApiConfig,
692-
Optional<ResteasyJaxrsConfigBuildItem> resteasyJaxrsConfig,
693690
OutputTargetBuildItem outputTargetBuildItem,
694691
List<IgnoreStaticDocumentBuildItem> ignoreStaticDocumentBuildItems) throws Exception {
695692
FilteredIndexView index = openApiFilteredIndexViewBuildItem.getIndex();
@@ -710,8 +707,7 @@ public void build(BuildProducer<FeatureBuildItem> feature,
710707
OpenAPI annotationModel;
711708

712709
if (shouldScanAnnotations(capabilities, index)) {
713-
annotationModel = generateAnnotationModel(index, capabilities, httpRootPathBuildItem, resteasyJaxrsConfig, config,
714-
openApiConfig);
710+
annotationModel = generateAnnotationModel(index, capabilities, httpRootPathBuildItem, config, openApiConfig);
715711
} else {
716712
annotationModel = new OpenAPIImpl();
717713
}
@@ -827,29 +823,24 @@ private OpenAPI generateStaticModel(SmallRyeOpenApiConfig smallRyeOpenApiConfig,
827823

828824
private OpenAPI generateAnnotationModel(IndexView indexView, Capabilities capabilities,
829825
HttpRootPathBuildItem httpRootPathBuildItem,
830-
Optional<ResteasyJaxrsConfigBuildItem> resteasyJaxrsConfig, Config config, OpenApiConfig openApiConfig) {
826+
Config config, OpenApiConfig openApiConfig) {
831827

832828
List<AnnotationScannerExtension> extensions = new ArrayList<>();
833829

834830
// Add the RESTEasy extension if the capability is present
835-
String defaultPath = httpRootPathBuildItem.getRootPath();
831+
String rootPath = httpRootPathBuildItem.getRootPath();
832+
String appPath = "";
833+
836834
if (capabilities.isPresent(Capability.RESTEASY)) {
837835
extensions.add(new RESTEasyExtension(indexView));
838-
if (resteasyJaxrsConfig.isPresent()) {
839-
defaultPath = resteasyJaxrsConfig.get().getRootPath();
840-
}
836+
appPath = config.getOptionalValue("quarkus.resteasy.path", String.class).orElse("");
841837
} else if (capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
842838
extensions.add(new RESTEasyExtension(indexView));
843839
openApiConfig.doAllowNakedPathParameter();
844-
Optional<String> maybePath = config.getOptionalValue("quarkus.resteasy-reactive.path", String.class);
845-
if (maybePath.isPresent()) {
846-
defaultPath = maybePath.get();
847-
}
840+
appPath = config.getOptionalValue("quarkus.resteasy-reactive.path", String.class).orElse("");
848841
}
849842

850-
if (defaultPath != null && !"/".equals(defaultPath)) {
851-
extensions.add(new CustomPathExtension(defaultPath));
852-
}
843+
extensions.add(new CustomPathExtension(rootPath, appPath));
853844

854845
OpenApiAnnotationScanner openApiAnnotationScanner = new OpenApiAnnotationScanner(openApiConfig, indexView, extensions);
855846
return openApiAnnotationScanner.scan(getScanners(capabilities, indexView));
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.quarkus.smallrye.openapi.deployment;
2+
3+
import java.io.IOException;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
import org.jboss.jandex.Index;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.params.ParameterizedTest;
11+
import org.junit.jupiter.params.provider.CsvSource;
12+
import org.mockito.Mockito;
13+
14+
import io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner;
15+
16+
class CustomPathExtensionTest {
17+
18+
AnnotationScanner scanner;
19+
20+
@BeforeEach
21+
void setup() {
22+
scanner = Mockito.mock(AnnotationScanner.class);
23+
}
24+
25+
@Test
26+
void testContextPathNotInvokedForEmptyPaths() {
27+
CustomPathExtension ext = new CustomPathExtension("/", "");
28+
ext.processScannerApplications(scanner, Collections.emptyList());
29+
Mockito.verify(scanner, Mockito.never()).setContextRoot(Mockito.anyString());
30+
}
31+
32+
@ParameterizedTest
33+
@CsvSource({
34+
"'/root', 'app-from-config' , '/root/app-from-config'",
35+
"'/' , '/app-from-config/', '/app-from-config'",
36+
"'' , '/app-from-config/', '/app-from-config'",
37+
" , 'app-from-config' , '/app-from-config'",
38+
})
39+
void testContextPathGenerationWithoutApplicationPathAnnotation(String rootPath, String appPath, String expected) {
40+
CustomPathExtension ext = new CustomPathExtension(rootPath, appPath);
41+
ext.processScannerApplications(scanner, Collections.emptyList());
42+
Mockito.verify(scanner).setContextRoot(expected);
43+
}
44+
45+
@ParameterizedTest
46+
@CsvSource({
47+
"'/root', 'app-from-config' , 1, '/root'",
48+
"'/' , '/app-from-config/', 0, ",
49+
"'' , '/app-from-config/', 0, ",
50+
" , 'app-from-config' , 0, ",
51+
})
52+
void testContextPathGenerationWithApplicationPathAnnotation(String rootPath, String appPath, int times, String expected)
53+
throws IOException {
54+
@jakarta.ws.rs.ApplicationPath("app-path-from-anno")
55+
class TestApp extends jakarta.ws.rs.core.Application {
56+
}
57+
58+
CustomPathExtension ext = new CustomPathExtension(rootPath, appPath);
59+
ext.processScannerApplications(scanner, List.of(Index.of(TestApp.class).getClassByName(TestApp.class)));
60+
Mockito.verify(scanner, Mockito.times(times)).setContextRoot(times > 0 ? expected : Mockito.anyString());
61+
}
62+
63+
}

0 commit comments

Comments
 (0)