diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index c078597d887f5..fd494a0867252 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -55,7 +55,7 @@
3.9.1
4.1.0
4.0.0
- 3.10.0
+ 3.12.0
2.10.0
6.4.0
4.6.0
diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtension.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtension.java
index a767883a999ff..611fd9cb23122 100644
--- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtension.java
+++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtension.java
@@ -15,13 +15,10 @@
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
-import io.smallrye.openapi.runtime.scanner.AnnotationScannerExtension;
-import io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner;
-
/**
* This adds support for the quarkus.http.root-path config option
*/
-public class CustomPathExtension implements AnnotationScannerExtension {
+public class CustomPathExtension {
static final Set APPLICATION_PATH = new TreeSet<>(Arrays.asList(
DotName.createSimple("jakarta.ws.rs.ApplicationPath"),
@@ -35,8 +32,7 @@ public CustomPathExtension(String rootPath, String appPath) {
this.appPath = appPath;
}
- @Override
- public void processScannerApplications(AnnotationScanner scanner, Collection applications) {
+ public String resolveContextRoot(Collection applications) {
Optional appPathAnnotationValue = applications.stream()
.flatMap(app -> APPLICATION_PATH.stream().map(app::declaredAnnotation))
.filter(Objects::nonNull)
@@ -48,15 +44,13 @@ public void processScannerApplications(AnnotationScanner scanner, Collection buildContextRpot(rootPath))
- .orElseGet(() -> buildContextRpot(rootPath, this.appPath));
+ String contextRoot = appPathAnnotationValue.map(path -> buildContextRoot(rootPath))
+ .orElseGet(() -> buildContextRoot(rootPath, this.appPath));
- if (!"/".equals(contextRoot)) {
- scanner.setContextRoot(contextRoot);
- }
+ return "/".equals(contextRoot) ? null : contextRoot;
}
- static String buildContextRpot(String... segments) {
+ static String buildContextRoot(String... segments) {
String path = Stream.of(segments)
.filter(Objects::nonNull)
.map(CustomPathExtension::stripSlashes)
diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/MediaTypeConfigSource.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/MediaTypeConfigSource.java
new file mode 100644
index 0000000000000..ee27703b5c534
--- /dev/null
+++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/MediaTypeConfigSource.java
@@ -0,0 +1,39 @@
+package io.quarkus.smallrye.openapi.deployment;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.microprofile.config.spi.ConfigSource;
+
+import io.smallrye.openapi.api.SmallRyeOASConfig;
+
+public class MediaTypeConfigSource implements ConfigSource {
+
+ private final Map mediaTypes = new HashMap<>();
+
+ public MediaTypeConfigSource() {
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_PRODUCES_STREAMING, "application/octet-stream");
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_CONSUMES_STREAMING, "application/octet-stream");
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_PRODUCES, "application/json");
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_CONSUMES, "application/json");
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_PRODUCES_PRIMITIVES, "text/plain");
+ mediaTypes.put(SmallRyeOASConfig.DEFAULT_CONSUMES_PRIMITIVES, "text/plain");
+ }
+
+ @Override
+ public Set getPropertyNames() {
+ return mediaTypes.keySet();
+ }
+
+ @Override
+ public String getValue(String propertyName) {
+ return mediaTypes.get(propertyName);
+ }
+
+ @Override
+ public String getName() {
+ return getClass().getSimpleName();
+ }
+
+}
diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/RESTEasyExtension.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/RESTEasyExtension.java
index ee71c46f19bd9..29a9ba9d28738 100644
--- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/RESTEasyExtension.java
+++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/RESTEasyExtension.java
@@ -12,9 +12,8 @@
import org.jboss.jandex.Type;
import io.quarkus.deployment.util.ServiceUtil;
-import io.smallrye.openapi.runtime.scanner.AnnotationScannerExtension;
-public class RESTEasyExtension implements AnnotationScannerExtension {
+public class RESTEasyExtension {
private static final DotName DOTNAME_PROVIDER = DotName.createSimple("jakarta.ws.rs.ext.Provider");
private static final DotName DOTNAME_ASYNC_RESPONSE_PROVIDER = DotName
@@ -90,7 +89,6 @@ private void scanAsyncResponseProviders(IndexView index) {
}
}
- @Override
public Type resolveAsyncType(Type type) {
if (type.kind() == Type.Kind.PARAMETERIZED_TYPE
&& asyncTypes.contains(type.name())) {
diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java
index c7154a5b83acd..a068cc44304cd 100644
--- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java
+++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java
@@ -8,7 +8,6 @@
import java.io.UncheckedIOException;
import java.lang.reflect.Modifier;
import java.net.URL;
-import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -16,10 +15,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
-import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -27,12 +24,18 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
@@ -42,7 +45,6 @@
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
-import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.eclipse.microprofile.openapi.spi.OASFactoryResolver;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
@@ -94,6 +96,7 @@
import io.quarkus.smallrye.openapi.deployment.filter.AutoServerFilter;
import io.quarkus.smallrye.openapi.deployment.filter.AutoTagFilter;
import io.quarkus.smallrye.openapi.deployment.filter.ClassAndMethod;
+import io.quarkus.smallrye.openapi.deployment.filter.DefaultInfoFilter;
import io.quarkus.smallrye.openapi.deployment.filter.SecurityConfigFilter;
import io.quarkus.smallrye.openapi.deployment.spi.AddToOpenAPIDefinitionBuildItem;
import io.quarkus.smallrye.openapi.deployment.spi.IgnoreStaticDocumentBuildItem;
@@ -115,19 +118,12 @@
import io.quarkus.vertx.http.deployment.spi.RouteBuildItem;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
import io.smallrye.openapi.api.OpenApiConfig;
-import io.smallrye.openapi.api.OpenApiConfigImpl;
import io.smallrye.openapi.api.OpenApiDocument;
+import io.smallrye.openapi.api.SmallRyeOpenAPI;
import io.smallrye.openapi.api.constants.SecurityConstants;
-import io.smallrye.openapi.api.models.OpenAPIImpl;
import io.smallrye.openapi.api.util.MergeUtil;
import io.smallrye.openapi.jaxrs.JaxRsConstants;
-import io.smallrye.openapi.runtime.OpenApiProcessor;
-import io.smallrye.openapi.runtime.OpenApiStaticFile;
-import io.smallrye.openapi.runtime.io.Format;
-import io.smallrye.openapi.runtime.io.OpenApiSerializer;
-import io.smallrye.openapi.runtime.scanner.AnnotationScannerExtension;
import io.smallrye.openapi.runtime.scanner.FilteredIndexView;
-import io.smallrye.openapi.runtime.scanner.OpenApiAnnotationScanner;
import io.smallrye.openapi.runtime.util.JandexUtil;
import io.smallrye.openapi.spring.SpringConstants;
import io.smallrye.openapi.vertx.VertxConstants;
@@ -139,16 +135,13 @@
* The main OpenAPI Processor. This will scan for JAX-RS, Spring and Vert.x Annotations, and, if any, add supplied schemas.
* The result is added to the deployable unit to be loaded at runtime.
*/
+@SuppressWarnings("deprecation")
public class SmallRyeOpenApiProcessor {
private static final Logger log = Logger.getLogger("io.quarkus.smallrye.openapi");
- private static final String META_INF_OPENAPI_YAML = "META-INF/openapi.yaml";
- private static final String WEB_INF_CLASSES_META_INF_OPENAPI_YAML = "WEB-INF/classes/META-INF/openapi.yaml";
- private static final String META_INF_OPENAPI_YML = "META-INF/openapi.yml";
- private static final String WEB_INF_CLASSES_META_INF_OPENAPI_YML = "WEB-INF/classes/META-INF/openapi.yml";
- private static final String META_INF_OPENAPI_JSON = "META-INF/openapi.json";
- private static final String WEB_INF_CLASSES_META_INF_OPENAPI_JSON = "WEB-INF/classes/META-INF/openapi.json";
+ private static final String META_INF_OPENAPI = "META-INF/openapi.";
+ private static final String WEB_INF_CLASSES_META_INF_OPENAPI = "WEB-INF/classes/META-INF/openapi.";
private static final DotName OPENAPI_SCHEMA = DotName.createSimple(Schema.class.getName());
private static final DotName OPENAPI_RESPONSE = DotName.createSimple(APIResponse.class.getName());
@@ -168,17 +161,6 @@ public class SmallRyeOpenApiProcessor {
private static final String MANAGEMENT_ENABLED = "quarkus.smallrye-openapi.management.enabled";
- static {
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_PRODUCES_STREAMING,
- "application/octet-stream");
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_CONSUMES_STREAMING,
- "application/octet-stream");
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_PRODUCES, "application/json");
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_CONSUMES, "application/json");
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_PRODUCES_PRIMITIVES, "text/plain");
- System.setProperty(io.smallrye.openapi.api.constants.OpenApiConstants.DEFAULT_CONSUMES_PRIMITIVES, "text/plain");
- }
-
@BuildStep
void contributeClassesToIndex(BuildProducer additionalIndexedClasses) {
// contribute additional JDK classes to the index, because SmallRye OpenAPI will check if some
@@ -200,7 +182,7 @@ void registerNativeImageResources(BuildProducer servic
void configFiles(BuildProducer watchedFiles,
SmallRyeOpenApiConfig openApiConfig,
LaunchModeBuildItem launchMode,
- OutputTargetBuildItem outputTargetBuildItem) throws IOException {
+ OutputTargetBuildItem outputTargetBuildItem) {
// Add any additional directories if configured
if (launchMode.getLaunchMode().isDevOrTest() && openApiConfig.additionalDocsDirectory.isPresent()) {
List additionalStaticDocuments = openApiConfig.additionalDocsDirectory.get();
@@ -213,12 +195,10 @@ void configFiles(BuildProducer watchedFiles,
}
}
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(META_INF_OPENAPI_YAML));
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(WEB_INF_CLASSES_META_INF_OPENAPI_YAML));
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(META_INF_OPENAPI_YML));
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(WEB_INF_CLASSES_META_INF_OPENAPI_YML));
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(META_INF_OPENAPI_JSON));
- watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(WEB_INF_CLASSES_META_INF_OPENAPI_JSON));
+ Stream.of("json", "yaml", "yml").forEach(ext -> {
+ watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(META_INF_OPENAPI + ext));
+ watchedFiles.produce(new HotDeploymentWatchedFileBuildItem(WEB_INF_CLASSES_META_INF_OPENAPI + ext));
+ });
}
@BuildStep
@@ -254,9 +234,8 @@ void registerAnnotatedUserDefinedRuntimeFilters(BuildProducer userDefinedRuntimeFilters = getUserDefinedRuntimeFilters(openApiConfig,
+ List userDefinedRuntimeFilters = getUserDefinedRuntimeFilters(config,
apiFilteredIndexViewBuildItem.getIndex());
syntheticBeans.produce(SyntheticBeanBuildItem.configure(OpenApiRecorder.UserDefinedRuntimeFilters.class)
@@ -462,13 +441,10 @@ private List getUserDefinedBuildtimeFilters(IndexView index) {
return getUserDefinedFilters(index, OpenApiFilter.RunStage.BUILD);
}
- private List getUserDefinedRuntimeFilters(OpenApiConfig openApiConfig, IndexView index) {
+ private List getUserDefinedRuntimeFilters(Config config, IndexView index) {
List userDefinedFilters = getUserDefinedFilters(index, OpenApiFilter.RunStage.RUN);
// Also add the MP way
- String filter = openApiConfig.filter();
- if (filter != null) {
- userDefinedFilters.add(filter);
- }
+ config.getOptionalValue(OASConfig.FILTER, String.class).ifPresent(userDefinedFilters::add);
return userDefinedFilters;
}
@@ -616,9 +592,7 @@ private Map> getRolesAllowedMethodReferences(OpenApiFiltere
.stream()
.map(index::getAnnotations)
.flatMap(Collection::stream)
- .flatMap((t) -> {
- return getMethods(t, index);
- })
+ .flatMap(t -> getMethods(t, index))
.collect(Collectors.toMap(
e -> JandexUtil.createUniqueMethodReference(e.getKey().declaringClass(), e.getKey()),
e -> List.of(e.getValue().value().asStringArray()),
@@ -639,9 +613,7 @@ private List getPermissionsAllowedMethodReferences(
return index
.getAnnotations(DotName.createSimple(PermissionsAllowed.class))
.stream()
- .flatMap((t) -> {
- return getMethods(t, index);
- })
+ .flatMap(t -> getMethods(t, index))
.map(e -> JandexUtil.createUniqueMethodReference(e.getKey().declaringClass(), e.getKey()))
.distinct()
.toList();
@@ -652,9 +624,7 @@ private List getAuthenticatedMethodReferences(OpenApiFilteredIndexViewBu
return index
.getAnnotations(DotName.createSimple(Authenticated.class.getName()))
.stream()
- .flatMap((t) -> {
- return getMethods(t, index);
- })
+ .flatMap(t -> getMethods(t, index))
.map(e -> JandexUtil.createUniqueMethodReference(e.getKey().declaringClass(), e.getKey()))
.distinct()
.toList();
@@ -864,43 +834,98 @@ public void build(BuildProducer feature,
Capabilities capabilities,
List openAPIBuildItems,
HttpRootPathBuildItem httpRootPathBuildItem,
- OutputTargetBuildItem out,
SmallRyeOpenApiConfig smallRyeOpenApiConfig,
OutputTargetBuildItem outputTargetBuildItem,
- List ignoreStaticDocumentBuildItems) throws IOException {
- FilteredIndexView index = openApiFilteredIndexViewBuildItem.getIndex();
+ List ignoreStaticDocumentBuildItems) {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ FilteredIndexView index = openApiFilteredIndexViewBuildItem.getIndex();
Config config = ConfigProvider.getConfig();
- OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
feature.produce(new FeatureBuildItem(Feature.SMALLRYE_OPENAPI));
- List urlIgnorePatterns = new ArrayList<>();
- for (IgnoreStaticDocumentBuildItem isdbi : ignoreStaticDocumentBuildItems) {
- urlIgnorePatterns.add(isdbi.getUrlIgnorePattern());
- }
-
- OpenAPI staticModel = generateStaticModel(smallRyeOpenApiConfig, urlIgnorePatterns,
- outputTargetBuildItem.getOutputDirectory(), openApiConfig);
+ List urlIgnorePatterns = ignoreStaticDocumentBuildItems.stream()
+ .map(IgnoreStaticDocumentBuildItem::getUrlIgnorePattern)
+ .toList();
- OpenAPI annotationModel;
+ SmallRyeOpenAPI.Builder builder = SmallRyeOpenAPI.builder()
+ .withConfig(config)
+ .withIndex(index)
+ .withApplicationClassLoader(loader)
+ .withScannerClassLoader(loader)
+ .enableModelReader(true)
+ .enableStandardStaticFiles(Boolean.FALSE.equals(smallRyeOpenApiConfig.ignoreStaticDocument))
+ .withResourceLocator(path -> {
+ URL locator = loader.getResource(path);
+ if (locator == null || shouldIgnore(urlIgnorePatterns, locator.toString())) {
+ return null;
+ }
+ return locator;
+ })
+ .withCustomStaticFile(() -> loadAdditionalDocsModel(smallRyeOpenApiConfig, urlIgnorePatterns,
+ outputTargetBuildItem.getOutputDirectory()))
+ .enableAnnotationScan(shouldScanAnnotations(capabilities, index))
+ .withScannerFilter(getScannerFilter(capabilities, index))
+ .withContextRootResolver(getContextRootResolver(config, capabilities, httpRootPathBuildItem))
+ .withTypeConverter(getTypeConverter(index, capabilities))
+ .enableUnannotatedPathParameters(capabilities.isPresent(Capability.RESTEASY_REACTIVE))
+ .enableStandardFilter(false)
+ .withFilters(openAPIBuildItems.stream().map(AddToOpenAPIDefinitionBuildItem::getOASFilter).toList());
+
+ getUserDefinedBuildtimeFilters(index).forEach(builder::addFilterName);
+
+ // This should be the final filter to run
+ builder.addFilter(new DefaultInfoFilter(config));
+
+ SmallRyeOpenAPI openAPI = builder.build();
+
+ Stream.of(Map.> entry("JSON", openAPI::toJSON),
+ Map.> entry("YAML", openAPI::toYAML))
+ .forEach(format -> {
+ String name = OpenApiConstants.BASE_NAME + format.getKey();
+ byte[] data = format.getValue().get().getBytes(StandardCharsets.UTF_8);
+ resourceBuildItemBuildProducer.produce(new GeneratedResourceBuildItem(name, data));
+ nativeImageResources.produce(new NativeImageResourceBuildItem(name));
+ });
+
+ SmallRyeOpenAPI finalOpenAPI;
+ SmallRyeOpenAPI storedOpenAPI;
+
+ Supplier filterOnlyBuilder = () -> {
+ var runtimeFilterBuilder = SmallRyeOpenAPI.builder()
+ .enableModelReader(false)
+ .enableStandardStaticFiles(false)
+ .enableAnnotationScan(false)
+ .enableStandardFilter(false)
+ .withInitialModel(openAPI.model());
+ Optional.ofNullable(getAutoServerFilter(smallRyeOpenApiConfig, true, "Auto generated value"))
+ .ifPresent(runtimeFilterBuilder::addFilter);
+ return runtimeFilterBuilder;
+ };
- if (shouldScanAnnotations(capabilities, index)) {
- annotationModel = generateAnnotationModel(index, capabilities, httpRootPathBuildItem, config, openApiConfig);
- } else {
- annotationModel = new OpenAPIImpl();
+ try {
+ builder = filterOnlyBuilder.get();
+ getUserDefinedRuntimeFilters(config, index).forEach(builder::addFilterName);
+ storedOpenAPI = builder.build();
+ } catch (Exception e) {
+ // Try again without the user-defined runtime filters
+ storedOpenAPI = filterOnlyBuilder.get().build();
}
- OpenApiDocument finalDocument = loadDocument(staticModel, annotationModel, openAPIBuildItems, index);
- for (Format format : Format.values()) {
- String name = OpenApiConstants.BASE_NAME + format;
- byte[] schemaDocument = OpenApiSerializer.serialize(finalDocument.get(), format).getBytes(StandardCharsets.UTF_8);
- resourceBuildItemBuildProducer.produce(new GeneratedResourceBuildItem(name, schemaDocument));
- nativeImageResources.produce(new NativeImageResourceBuildItem(name));
- }
+ finalOpenAPI = storedOpenAPI;
+
+ smallRyeOpenApiConfig.storeSchemaDirectory.ifPresent(storageDir -> {
+ try {
+ storeGeneratedSchema(storageDir, outputTargetBuildItem, finalOpenAPI.toJSON(), "json");
+ storeGeneratedSchema(storageDir, outputTargetBuildItem, finalOpenAPI.toYAML(), "yaml");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
- OpenApiDocument finalStoredOpenApiDocument = storeDocument(out, smallRyeOpenApiConfig, index, finalDocument.get());
- openApiDocumentProducer.produce(new OpenApiDocumentBuildItem(finalStoredOpenApiDocument));
+ OpenApiDocument output = OpenApiDocument.newInstance();
+ output.set(finalOpenAPI.model());
+ openApiDocumentProducer.produce(new OpenApiDocumentBuildItem(output));
}
@BuildStep
@@ -929,10 +954,8 @@ private void produceReflectiveHierarchy(BuildProducer ignorePatterns, Path target,
- OpenApiConfig openApiConfig)
- throws IOException {
-
- if (smallRyeOpenApiConfig.ignoreStaticDocument) {
+ private InputStream loadAdditionalDocsModel(SmallRyeOpenApiConfig openApiConfig, List ignorePatterns,
+ Path target) {
+ if (openApiConfig.ignoreStaticDocument) {
return null;
- } else {
- List results = findStaticModels(smallRyeOpenApiConfig, ignorePatterns, target);
- if (!results.isEmpty()) {
- OpenAPI mergedStaticModel = new OpenAPIImpl();
- for (Result result : results) {
- try (InputStream is = result.inputStream;
- OpenApiStaticFile staticFile = new OpenApiStaticFile(is, result.format)) {
- OpenAPI staticFileModel = io.smallrye.openapi.runtime.OpenApiProcessor
- .modelFromStaticFile(openApiConfig, staticFile);
- mergedStaticModel = MergeUtil.mergeObjects(mergedStaticModel, staticFileModel);
- }
- }
- return mergedStaticModel;
- }
- return null;
- }
- }
-
- private OpenAPI generateAnnotationModel(IndexView indexView, Capabilities capabilities,
- HttpRootPathBuildItem httpRootPathBuildItem,
- Config config, OpenApiConfig openApiConfig) {
-
- List extensions = new ArrayList<>();
-
- // Add the RESTEasy extension if the capability is present
- String rootPath = httpRootPathBuildItem.getRootPath();
- String appPath = "";
-
- if (capabilities.isPresent(Capability.RESTEASY)) {
- extensions.add(new RESTEasyExtension(indexView));
- appPath = config.getOptionalValue("quarkus.resteasy.path", String.class).orElse("");
- } else if (capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
- extensions.add(new RESTEasyExtension(indexView));
- openApiConfig.doAllowNakedPathParameter();
- appPath = config.getOptionalValue("quarkus.rest.path", String.class).orElse("");
}
- extensions.add(new CustomPathExtension(rootPath, appPath));
+ SmallRyeOpenAPI.Builder staticBuilder = SmallRyeOpenAPI.builder()
+ .withConfig(ConfigProvider.getConfig())
+ .enableModelReader(false)
+ .enableStandardStaticFiles(false)
+ .enableAnnotationScan(false)
+ .enableStandardFilter(false);
- OpenApiAnnotationScanner openApiAnnotationScanner = new OpenApiAnnotationScanner(openApiConfig, indexView, extensions);
- return openApiAnnotationScanner.scan(getScanners(capabilities, indexView));
+ return openApiConfig.additionalDocsDirectory
+ .map(Collection::stream)
+ .orElseGet(Stream::empty)
+ .map(path -> getResourceFiles(path, target))
+ .flatMap(Collection::stream)
+ .filter(path -> path.endsWith(".json") || path.endsWith(".yaml") || path.endsWith(".yml"))
+ .flatMap(path -> loadResources(path, ignorePatterns))
+ .map(stream -> staticBuilder.withCustomStaticFile(() -> stream).build().model())
+ .reduce(MergeUtil::merge)
+ .map(mergedModel -> staticBuilder
+ .withInitialModel(mergedModel)
+ .withCustomStaticFile(() -> null)
+ .build()
+ .toJSON())
+ .map(jsonModel -> new ByteArrayInputStream(jsonModel.getBytes(StandardCharsets.UTF_8)))
+ .orElse(null);
}
- private String[] getScanners(Capabilities capabilities, IndexView index) {
+ private Predicate getScannerFilter(Capabilities capabilities, IndexView index) {
List scanners = new ArrayList<>();
if (capabilities.isPresent(Capability.RESTEASY) || capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
scanners.add(JAX_RS);
@@ -1038,78 +1043,59 @@ private String[] getScanners(Capabilities capabilities, IndexView index) {
if (isUsingVertxRoute(index)) {
scanners.add(VERT_X);
}
- return scanners.toArray(new String[] {});
+ return scanners::contains;
}
- private List findStaticModels(SmallRyeOpenApiConfig openApiConfig, List ignorePatterns, Path target) {
- List results = new ArrayList<>();
-
- // First check for the file in both META-INF and WEB-INF/classes/META-INF
- addStaticModelIfExist(results, ignorePatterns, Format.YAML, META_INF_OPENAPI_YAML);
- addStaticModelIfExist(results, ignorePatterns, Format.YAML, WEB_INF_CLASSES_META_INF_OPENAPI_YAML);
- addStaticModelIfExist(results, ignorePatterns, Format.YAML, META_INF_OPENAPI_YML);
- addStaticModelIfExist(results, ignorePatterns, Format.YAML, WEB_INF_CLASSES_META_INF_OPENAPI_YML);
- addStaticModelIfExist(results, ignorePatterns, Format.JSON, META_INF_OPENAPI_JSON);
- addStaticModelIfExist(results, ignorePatterns, Format.JSON, WEB_INF_CLASSES_META_INF_OPENAPI_JSON);
+ private Function, String> getContextRootResolver(Config config, Capabilities capabilities,
+ HttpRootPathBuildItem httpRootPathBuildItem) {
+ String rootPath = httpRootPathBuildItem.getRootPath();
+ String appPath = "";
- // Add any additional directories if configured
- if (openApiConfig.additionalDocsDirectory.isPresent()) {
- List additionalStaticDocuments = openApiConfig.additionalDocsDirectory.get();
- for (Path path : additionalStaticDocuments) {
- // Scan all yaml and json files
- try {
- List filesInDir = getResourceFiles(path, target);
- for (String possibleModelFile : filesInDir) {
- addStaticModelIfExist(results, ignorePatterns, possibleModelFile);
- }
- } catch (IOException ioe) {
- throw new UncheckedIOException("An error occurred while processing " + path, ioe);
- }
- }
+ if (capabilities.isPresent(Capability.RESTEASY)) {
+ appPath = config.getOptionalValue("quarkus.resteasy.path", String.class).orElse("");
+ } else if (capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
+ appPath = config.getOptionalValue("quarkus.rest.path", String.class).orElse("");
}
- return results;
+ var resolver = new CustomPathExtension(rootPath, appPath);
+ return resolver::resolveContextRoot;
}
- private void addStaticModelIfExist(List results, List ignorePatterns, String path) {
- if (path.endsWith(".json")) {
- // Scan a specific json file
- addStaticModelIfExist(results, ignorePatterns, Format.JSON, path);
- } else if (path.endsWith(".yaml") || path.endsWith(".yml")) {
- // Scan a specific yaml file
- addStaticModelIfExist(results, ignorePatterns, Format.YAML, path);
+ private UnaryOperator getTypeConverter(IndexView indexView, Capabilities capabilities) {
+ if (capabilities.isPresent(Capability.RESTEASY) || capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
+ return new RESTEasyExtension(indexView)::resolveAsyncType;
+ } else {
+ return UnaryOperator.identity();
}
}
- private void addStaticModelIfExist(List results, List ignorePatterns, Format format, String path) {
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ private Stream extends InputStream> loadResources(String path, List ignorePatterns) {
+ Spliterator resources;
try {
- Enumeration urls = cl.getResources(path);
- while (urls.hasMoreElements()) {
- URL url = urls.nextElement();
- // Check if we should ignore
- String urlAsString = url.toString();
- if (!shouldIgnore(ignorePatterns, urlAsString)) {
- // Add as static model
- URLConnection con = url.openConnection();
- con.setUseCaches(false);
- try (InputStream inputStream = con.getInputStream()) {
- if (inputStream != null) {
- byte[] contents = IoUtil.readBytes(inputStream);
-
- results.add(new Result(format, new ByteArrayInputStream(contents)));
- }
- } catch (IOException ex) {
- throw new UncheckedIOException("An error occurred while processing " + urlAsString + " for " + path,
- ex);
- }
- }
- }
-
+ var resourceEnum = Thread.currentThread().getContextClassLoader().getResources(path).asIterator();
+ resources = Spliterators.spliteratorUnknownSize(resourceEnum, Spliterator.IMMUTABLE);
} catch (IOException ex) {
- throw new UncheckedIOException(ex);
+ throw new UncheckedIOException("Exception processing resources for path " + path, ex);
}
+
+ return StreamSupport.stream(resources, false)
+ .filter(url -> !shouldIgnore(ignorePatterns, url.toString()))
+ .map(url -> loadResource(path, url))
+ .filter(Objects::nonNull);
+ }
+
+ private InputStream loadResource(String path, URL url) {
+ try (InputStream inputStream = url.openStream()) {
+ if (inputStream != null) {
+ byte[] contents = IoUtil.readBytes(inputStream);
+ return new ByteArrayInputStream(contents);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("An error occurred while processing %s for %s".formatted(url, path), e);
+ }
+
+ return null;
}
private boolean shouldIgnore(List ignorePatterns, String url) {
@@ -1122,7 +1108,7 @@ private boolean shouldIgnore(List ignorePatterns, String url) {
return false;
}
- private List getResourceFiles(Path resourcePath, Path target) throws IOException {
+ private List getResourceFiles(Path resourcePath, Path target) {
final String resourceName = ClassPathUtils.toResourceName(resourcePath);
List filenames = new ArrayList<>();
// Here we are resolving the resource dir relative to the classes dir and if it does not exist, we fall back to locating the resource dir on the classpath.
@@ -1132,6 +1118,8 @@ private List getResourceFiles(Path resourcePath, Path target) throws IOE
if (targetResourceDir != null && Files.exists(targetResourceDir)) {
try (Stream paths = Files.list(targetResourceDir)) {
return paths.map(t -> resourceName + "/" + t.getFileName().toString()).toList();
+ } catch (IOException e) {
+ throw new UncheckedIOException("An error occurred while processing " + resourcePath, e);
}
} else {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
@@ -1144,128 +1132,10 @@ private List getResourceFiles(Path resourcePath, Path target) throws IOE
}
}
}
+ } catch (IOException e) {
+ throw new UncheckedIOException("An error occurred while processing " + resourcePath, e);
}
}
return filenames;
}
-
- static class Result {
- final Format format;
- final InputStream inputStream;
-
- Result(Format format, InputStream inputStream) {
- this.format = format;
- this.inputStream = inputStream;
- }
- }
-
- private OpenApiDocument loadDocument(OpenAPI staticModel, OpenAPI annotationModel,
- List openAPIBuildItems, IndexView index) {
- OpenApiDocument document = prepareOpenApiDocument(staticModel, annotationModel, openAPIBuildItems, index, true);
-
- Config c = ConfigProvider.getConfig();
- String title = c.getOptionalValue("quarkus.application.name", String.class).orElse("Generated");
- String version = c.getOptionalValue("quarkus.application.version", String.class).orElse("1.0");
-
- document.archiveName(title);
- document.version(version);
-
- document.initialize();
- return document;
- }
-
- private OpenApiDocument storeDocument(OutputTargetBuildItem out,
- SmallRyeOpenApiConfig smallRyeOpenApiConfig,
- IndexView index,
- OpenAPI loadedModel) throws IOException {
- return storeDocument(out, smallRyeOpenApiConfig, index, loadedModel, true);
- }
-
- private OpenApiDocument storeDocument(OutputTargetBuildItem out,
- SmallRyeOpenApiConfig smallRyeOpenApiConfig,
- IndexView index,
- OpenAPI loadedModel,
- boolean includeRuntimeFilters) throws IOException {
-
- Config config = ConfigProvider.getConfig();
- OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
-
- OpenApiDocument document = prepareOpenApiDocument(loadedModel, null, Collections.emptyList(), index, false);
-
- if (includeRuntimeFilters) {
- List userDefinedRuntimeFilters = getUserDefinedRuntimeFilters(openApiConfig, index);
- for (String s : userDefinedRuntimeFilters) {
- document.filter(filter(s, index)); // This usually happens at runtime, so when storing we want to filter here too.
- }
- }
-
- // By default, also add the auto generated server
- OASFilter autoServerFilter = getAutoServerFilter(smallRyeOpenApiConfig, true, "Auto generated value");
- if (autoServerFilter != null) {
- document.filter(autoServerFilter);
- }
-
- try {
- document.initialize();
- } catch (RuntimeException re) {
- if (includeRuntimeFilters) {
- // This is a Runtime filter, so it might not work at build time. In that case we ignore the filter.
- return storeDocument(out, smallRyeOpenApiConfig, index, loadedModel, false);
- } else {
- throw re;
- }
- }
- // Store the document if needed
- boolean shouldStore = smallRyeOpenApiConfig.storeSchemaDirectory.isPresent();
- if (shouldStore) {
- for (Format format : Format.values()) {
- byte[] schemaDocument = OpenApiSerializer.serialize(document.get(), format).getBytes(StandardCharsets.UTF_8);
- storeGeneratedSchema(smallRyeOpenApiConfig, out, schemaDocument, format);
- }
- }
-
- return document;
- }
-
- private OpenApiDocument prepareOpenApiDocument(OpenAPI staticModel,
- OpenAPI annotationModel,
- List openAPIBuildItems,
- IndexView index,
- boolean executeBuildFilters) {
- Config config = ConfigProvider.getConfig();
- OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
-
- OpenAPI readerModel = OpenApiProcessor.modelFromReader(openApiConfig,
- Thread.currentThread().getContextClassLoader(), index);
-
- OpenApiDocument document = createDocument(openApiConfig);
- if (annotationModel != null) {
- document.modelFromAnnotations(annotationModel);
- }
- document.modelFromReader(readerModel);
- document.modelFromStaticFile(staticModel);
- for (AddToOpenAPIDefinitionBuildItem openAPIBuildItem : openAPIBuildItems) {
- OASFilter otherExtensionFilter = openAPIBuildItem.getOASFilter();
- document.filter(otherExtensionFilter);
- }
- // Add user defined Build time filters if necessary
- if (executeBuildFilters) {
- List userDefinedFilters = getUserDefinedBuildtimeFilters(index);
- for (String filter : userDefinedFilters) {
- document.filter(filter(filter, index));
- }
- }
- return document;
- }
-
- private OpenApiDocument createDocument(OpenApiConfig openApiConfig) {
- OpenApiDocument document = OpenApiDocument.INSTANCE;
- document.reset();
- document.config(openApiConfig);
- return document;
- }
-
- private OASFilter filter(String className, IndexView index) {
- return OpenApiProcessor.getFilter(className, Thread.currentThread().getContextClassLoader(), index);
- }
}
diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/filter/DefaultInfoFilter.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/filter/DefaultInfoFilter.java
new file mode 100644
index 0000000000000..1dbbbed3fc136
--- /dev/null
+++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/filter/DefaultInfoFilter.java
@@ -0,0 +1,35 @@
+package io.quarkus.smallrye.openapi.deployment.filter;
+
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.openapi.OASFactory;
+import org.eclipse.microprofile.openapi.OASFilter;
+import org.eclipse.microprofile.openapi.models.OpenAPI;
+import org.eclipse.microprofile.openapi.models.info.Info;
+
+public class DefaultInfoFilter implements OASFilter {
+
+ final Config config;
+
+ public DefaultInfoFilter(Config config) {
+ this.config = config;
+ }
+
+ @Override
+ public void filterOpenAPI(OpenAPI openAPI) {
+ Info info = openAPI.getInfo();
+
+ if (info == null) {
+ info = OASFactory.createInfo();
+ openAPI.setInfo(info);
+ }
+
+ if (info.getTitle() == null) {
+ String title = config.getOptionalValue("quarkus.application.name", String.class).orElse("Generated");
+ info.setTitle(title + " API");
+ }
+ if (info.getVersion() == null) {
+ String version = config.getOptionalValue("quarkus.application.version", String.class).orElse("1.0");
+ info.setVersion((version == null ? "1.0" : version));
+ }
+ }
+}
diff --git a/extensions/smallrye-openapi/deployment/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/extensions/smallrye-openapi/deployment/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
new file mode 100644
index 0000000000000..8195a1e2b5c7d
--- /dev/null
+++ b/extensions/smallrye-openapi/deployment/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
@@ -0,0 +1 @@
+io.quarkus.smallrye.openapi.deployment.MediaTypeConfigSource
\ No newline at end of file
diff --git a/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtensionTest.java b/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtensionTest.java
index ce08b78906828..3c60b349a6c0a 100644
--- a/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtensionTest.java
+++ b/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/deployment/CustomPathExtensionTest.java
@@ -1,32 +1,24 @@
package io.quarkus.smallrye.openapi.deployment;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.jboss.jandex.Index;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
-import org.mockito.Mockito;
-
-import io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner;
class CustomPathExtensionTest {
- AnnotationScanner scanner;
-
- @BeforeEach
- void setup() {
- scanner = Mockito.mock(AnnotationScanner.class);
- }
-
@Test
void testContextPathNotInvokedForEmptyPaths() {
CustomPathExtension ext = new CustomPathExtension("/", "");
- ext.processScannerApplications(scanner, Collections.emptyList());
- Mockito.verify(scanner, Mockito.never()).setContextRoot(Mockito.anyString());
+ String contextRoot = ext.resolveContextRoot(Collections.emptyList());
+ assertNull(contextRoot);
}
@ParameterizedTest
@@ -38,8 +30,8 @@ void testContextPathNotInvokedForEmptyPaths() {
})
void testContextPathGenerationWithoutApplicationPathAnnotation(String rootPath, String appPath, String expected) {
CustomPathExtension ext = new CustomPathExtension(rootPath, appPath);
- ext.processScannerApplications(scanner, Collections.emptyList());
- Mockito.verify(scanner).setContextRoot(expected);
+ String contextRoot = ext.resolveContextRoot(Collections.emptyList());
+ assertEquals(expected, contextRoot);
}
@ParameterizedTest
@@ -56,8 +48,8 @@ class TestApp extends jakarta.ws.rs.core.Application {
}
CustomPathExtension ext = new CustomPathExtension(rootPath, appPath);
- ext.processScannerApplications(scanner, List.of(Index.of(TestApp.class).getClassByName(TestApp.class)));
- Mockito.verify(scanner, Mockito.times(times)).setContextRoot(times > 0 ? expected : Mockito.anyString());
+ String contextRoot = ext.resolveContextRoot(List.of(Index.of(TestApp.class).getClassByName(TestApp.class)));
+ assertEquals(expected, contextRoot);
}
}
diff --git a/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/OpenApiWithConfigTestCase.java b/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/OpenApiWithConfigTestCase.java
index 1377a31316632..f37cb6cd6c58c 100644
--- a/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/OpenApiWithConfigTestCase.java
+++ b/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/OpenApiWithConfigTestCase.java
@@ -35,6 +35,6 @@ public void testOpenAPI() {
.body("paths", Matchers.hasKey("/openapi"))
.body("paths", Matchers.not(Matchers.hasKey("/resource")));
- System.clearProperty(io.smallrye.openapi.api.constants.OpenApiConstants.INFO_TITLE);
+ System.clearProperty(io.smallrye.openapi.api.SmallRyeOASConfig.INFO_TITLE);
}
}
diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiConfigMapping.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiConfigMapping.java
index 2a9e85afd638a..9d1fdb11ad2ce 100644
--- a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiConfigMapping.java
+++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiConfigMapping.java
@@ -5,17 +5,20 @@
import java.util.Map;
import org.eclipse.microprofile.config.spi.Converter;
+import org.eclipse.microprofile.openapi.OASConfig;
import io.smallrye.config.ConfigSourceInterceptorContext;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.Converters;
import io.smallrye.config.RelocateConfigSourceInterceptor;
import io.smallrye.openapi.api.OpenApiConfig.OperationIdStrategy;
+import io.smallrye.openapi.api.SmallRyeOASConfig;
/**
* Maps config from MicroProfile and SmallRye to Quarkus
*/
public class OpenApiConfigMapping extends RelocateConfigSourceInterceptor {
+ private static final long serialVersionUID = 1L;
private static final Map RELOCATIONS = relocations();
private static final Converter OPERATION_ID_STRATEGY_CONVERTER = Converters
.getImplicitConverter(OperationIdStrategy.class);
@@ -27,31 +30,30 @@ public OpenApiConfigMapping() {
@Override
public ConfigValue getValue(ConfigSourceInterceptorContext context, String name) {
ConfigValue configValue = super.getValue(context, name);
+
// Special case for enum. The converter run after the interceptors, so we have to do this here.
- if (name.equals(io.smallrye.openapi.api.constants.OpenApiConstants.OPERATION_ID_STRAGEGY)) {
- if (configValue != null) {
- String correctValue = OPERATION_ID_STRATEGY_CONVERTER.convert(configValue.getValue()).toString();
- configValue = configValue.withValue(correctValue);
- }
+ if (configValue != null && name.equals(SmallRyeOASConfig.OPERATION_ID_STRAGEGY)) {
+ String correctValue = OPERATION_ID_STRATEGY_CONVERTER.convert(configValue.getValue()).toString();
+ configValue = configValue.withValue(correctValue);
}
+
return configValue;
}
private static Map relocations() {
Map relocations = new HashMap<>();
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.VERSION, QUARKUS_OPEN_API_VERSION);
- mapKey(relocations, org.eclipse.microprofile.openapi.OASConfig.SERVERS, QUARKUS_SERVERS);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_TITLE, QUARKUS_INFO_TITLE);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_VERSION, QUARKUS_INFO_VERSION);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_DESCRIPTION, QUARKUS_INFO_DESCRIPTION);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_TERMS, QUARKUS_INFO_TERMS);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_CONTACT_EMAIL, QUARKUS_INFO_CONTACT_EMAIL);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_CONTACT_NAME, QUARKUS_INFO_CONTACT_NAME);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_CONTACT_URL, QUARKUS_INFO_CONTACT_URL);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_LICENSE_NAME, QUARKUS_INFO_LICENSE_NAME);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.INFO_LICENSE_URL, QUARKUS_INFO_LICENSE_URL);
- mapKey(relocations, io.smallrye.openapi.api.constants.OpenApiConstants.OPERATION_ID_STRAGEGY,
- QUARKUS_OPERATION_ID_STRATEGY);
+ mapKey(relocations, SmallRyeOASConfig.VERSION, QUARKUS_OPEN_API_VERSION);
+ mapKey(relocations, OASConfig.SERVERS, QUARKUS_SERVERS);
+ mapKey(relocations, SmallRyeOASConfig.INFO_TITLE, QUARKUS_INFO_TITLE);
+ mapKey(relocations, SmallRyeOASConfig.INFO_VERSION, QUARKUS_INFO_VERSION);
+ mapKey(relocations, SmallRyeOASConfig.INFO_DESCRIPTION, QUARKUS_INFO_DESCRIPTION);
+ mapKey(relocations, SmallRyeOASConfig.INFO_TERMS, QUARKUS_INFO_TERMS);
+ mapKey(relocations, SmallRyeOASConfig.INFO_CONTACT_EMAIL, QUARKUS_INFO_CONTACT_EMAIL);
+ mapKey(relocations, SmallRyeOASConfig.INFO_CONTACT_NAME, QUARKUS_INFO_CONTACT_NAME);
+ mapKey(relocations, SmallRyeOASConfig.INFO_CONTACT_URL, QUARKUS_INFO_CONTACT_URL);
+ mapKey(relocations, SmallRyeOASConfig.INFO_LICENSE_NAME, QUARKUS_INFO_LICENSE_NAME);
+ mapKey(relocations, SmallRyeOASConfig.INFO_LICENSE_URL, QUARKUS_INFO_LICENSE_URL);
+ mapKey(relocations, SmallRyeOASConfig.OPERATION_ID_STRAGEGY, QUARKUS_OPERATION_ID_STRATEGY);
return Collections.unmodifiableMap(relocations);
}
diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java
index 99b85cdd96750..8d9676a3c0171 100644
--- a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java
+++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java
@@ -2,27 +2,21 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.openapi.OASConfig;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.OpenAPI;
-import org.jboss.jandex.IndexView;
-import org.jboss.jandex.Indexer;
import io.quarkus.smallrye.openapi.runtime.filter.DisabledRestEndpointsFilter;
-import io.smallrye.openapi.api.OpenApiConfig;
-import io.smallrye.openapi.api.OpenApiConfigImpl;
-import io.smallrye.openapi.api.OpenApiDocument;
-import io.smallrye.openapi.runtime.OpenApiProcessor;
-import io.smallrye.openapi.runtime.OpenApiStaticFile;
-import io.smallrye.openapi.runtime.io.Format;
-import io.smallrye.openapi.runtime.io.OpenApiSerializer;
+import io.smallrye.openapi.api.SmallRyeOpenAPI;
/**
* Loads the document and make it available
@@ -30,17 +24,31 @@
@ApplicationScoped
public class OpenApiDocumentService implements OpenApiDocumentHolder {
- private static final IndexView EMPTY_INDEX = new Indexer().complete();
private final OpenApiDocumentHolder documentHolder;
@Inject
public OpenApiDocumentService(OASFilter autoSecurityFilter,
OpenApiRecorder.UserDefinedRuntimeFilters userDefinedRuntimeFilters, Config config) {
- if (config.getOptionalValue("quarkus.smallrye-openapi.always-run-filter", Boolean.class).orElse(Boolean.FALSE)) {
- this.documentHolder = new DynamicDocument(config, autoSecurityFilter, userDefinedRuntimeFilters.filters());
- } else {
- this.documentHolder = new StaticDocument(config, autoSecurityFilter, userDefinedRuntimeFilters.filters());
+ ClassLoader loader = Optional.ofNullable(OpenApiConstants.classLoader)
+ .orElseGet(Thread.currentThread()::getContextClassLoader);
+
+ try (InputStream source = loader.getResourceAsStream(OpenApiConstants.BASE_NAME + "JSON")) {
+ if (source != null) {
+ var userFilters = userDefinedRuntimeFilters.filters();
+ boolean dynamic = config.getOptionalValue("quarkus.smallrye-openapi.always-run-filter", Boolean.class)
+ .orElse(Boolean.FALSE);
+
+ if (dynamic) {
+ this.documentHolder = new DynamicDocument(source, config, autoSecurityFilter, userFilters);
+ } else {
+ this.documentHolder = new StaticDocument(source, config, autoSecurityFilter, userFilters);
+ }
+ } else {
+ this.documentHolder = new EmptyDocument();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
}
@@ -52,6 +60,18 @@ public byte[] getYamlDocument() {
return this.documentHolder.getYamlDocument();
}
+ static class EmptyDocument implements OpenApiDocumentHolder {
+ static final byte[] EMPTY = new byte[0];
+
+ public byte[] getJsonDocument() {
+ return EMPTY;
+ }
+
+ public byte[] getYamlDocument() {
+ return EMPTY;
+ }
+ }
+
/**
* Generate the document once on creation.
*/
@@ -60,39 +80,22 @@ static class StaticDocument implements OpenApiDocumentHolder {
private byte[] jsonDocument;
private byte[] yamlDocument;
- StaticDocument(Config config, OASFilter autoFilter, List userFilters) {
- ClassLoader cl = OpenApiConstants.classLoader == null ? Thread.currentThread().getContextClassLoader()
- : OpenApiConstants.classLoader;
- try (InputStream is = cl.getResourceAsStream(OpenApiConstants.BASE_NAME + Format.JSON)) {
- if (is != null) {
- try (OpenApiStaticFile staticFile = new OpenApiStaticFile(is, Format.JSON)) {
-
- OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
-
- OpenApiDocument document = OpenApiDocument.INSTANCE;
- document.reset();
- document.config(openApiConfig);
- document.modelFromStaticFile(OpenApiProcessor.modelFromStaticFile(openApiConfig, staticFile));
- if (autoFilter != null) {
- document.filter(autoFilter);
- }
- document.filter(new DisabledRestEndpointsFilter());
- for (String userFilter : userFilters) {
- document.filter(OpenApiProcessor.getFilter(userFilter, cl, EMPTY_INDEX));
- }
- document.initialize();
-
- this.jsonDocument = OpenApiSerializer.serialize(document.get(), Format.JSON)
- .getBytes(StandardCharsets.UTF_8);
- this.yamlDocument = OpenApiSerializer.serialize(document.get(), Format.YAML)
- .getBytes(StandardCharsets.UTF_8);
- document.reset();
- document = null;
- }
- }
- } catch (IOException ex) {
- throw new RuntimeException("Could not find [" + OpenApiConstants.BASE_NAME + Format.JSON + "]");
- }
+ StaticDocument(InputStream source, Config config, OASFilter autoFilter, List userFilters) {
+ SmallRyeOpenAPI.Builder builder = SmallRyeOpenAPI.builder()
+ .withConfig(config)
+ .enableModelReader(false)
+ .enableStandardStaticFiles(false)
+ .enableAnnotationScan(false)
+ .enableStandardFilter(false)
+ .withCustomStaticFile(() -> source);
+
+ Optional.ofNullable(autoFilter).ifPresent(builder::addFilter);
+ builder.addFilter(new DisabledRestEndpointsFilter());
+ userFilters.forEach(builder::addFilterName);
+
+ SmallRyeOpenAPI openAPI = builder.build();
+ jsonDocument = openAPI.toJSON().getBytes(StandardCharsets.UTF_8);
+ yamlDocument = openAPI.toYAML().getBytes(StandardCharsets.UTF_8);
}
public byte[] getJsonDocument() {
@@ -109,78 +112,35 @@ public byte[] getYamlDocument() {
*/
static class DynamicDocument implements OpenApiDocumentHolder {
+ private SmallRyeOpenAPI.Builder builder;
private OpenAPI generatedOnBuild;
- private OpenApiConfig openApiConfig;
- private List userFilters = new ArrayList<>();
- private OASFilter autoFilter;
- private DisabledRestEndpointsFilter disabledEndpointsFilter;
-
- DynamicDocument(Config config, OASFilter autoFilter, List annotatedUserFilters) {
- ClassLoader cl = OpenApiConstants.classLoader == null ? Thread.currentThread().getContextClassLoader()
- : OpenApiConstants.classLoader;
- try (InputStream is = cl.getResourceAsStream(OpenApiConstants.BASE_NAME + Format.JSON)) {
- if (is != null) {
- try (OpenApiStaticFile staticFile = new OpenApiStaticFile(is, Format.JSON)) {
- this.openApiConfig = new OpenApiConfigImpl(config);
- OASFilter microProfileDefinedFilter = OpenApiProcessor.getFilter(openApiConfig, cl, EMPTY_INDEX);
- if (microProfileDefinedFilter != null) {
- userFilters.add(microProfileDefinedFilter);
- }
- for (String annotatedUserFilter : annotatedUserFilters) {
- OASFilter annotatedUserDefinedFilter = OpenApiProcessor.getFilter(annotatedUserFilter, cl,
- EMPTY_INDEX);
- userFilters.add(annotatedUserDefinedFilter);
- }
- this.autoFilter = autoFilter;
- this.generatedOnBuild = OpenApiProcessor.modelFromStaticFile(this.openApiConfig, staticFile);
- this.disabledEndpointsFilter = new DisabledRestEndpointsFilter();
- }
- }
- } catch (IOException ex) {
- throw new RuntimeException("Could not find [" + OpenApiConstants.BASE_NAME + Format.JSON + "]");
- }
+
+ DynamicDocument(InputStream source, Config config, OASFilter autoFilter, List annotatedUserFilters) {
+ builder = SmallRyeOpenAPI.builder()
+ .withConfig(config)
+ .enableModelReader(false)
+ .enableStandardStaticFiles(false)
+ .enableAnnotationScan(false)
+ .enableStandardFilter(false)
+ .withCustomStaticFile(() -> source);
+
+ generatedOnBuild = builder.build().model();
+
+ builder.withCustomStaticFile(() -> null);
+ builder.withInitialModel(generatedOnBuild);
+
+ Optional.ofNullable(autoFilter).ifPresent(builder::addFilter);
+ builder.addFilter(new DisabledRestEndpointsFilter());
+ config.getOptionalValue(OASConfig.FILTER, String.class).ifPresent(builder::addFilterName);
+ annotatedUserFilters.forEach(builder::addFilterName);
}
public byte[] getJsonDocument() {
- try {
- OpenApiDocument document = getOpenApiDocument();
- byte[] jsonDocument = OpenApiSerializer.serialize(document.get(), Format.JSON)
- .getBytes(StandardCharsets.UTF_8);
- document.reset();
- document = null;
- return jsonDocument;
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
+ return builder.build().toJSON().getBytes(StandardCharsets.UTF_8);
}
public byte[] getYamlDocument() {
- try {
- OpenApiDocument document = getOpenApiDocument();
- byte[] yamlDocument = OpenApiSerializer.serialize(document.get(), Format.YAML)
- .getBytes(StandardCharsets.UTF_8);
- document.reset();
- document = null;
- return yamlDocument;
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private OpenApiDocument getOpenApiDocument() {
- OpenApiDocument document = OpenApiDocument.INSTANCE;
- document.reset();
- document.config(this.openApiConfig);
- document.modelFromStaticFile(this.generatedOnBuild);
- if (this.autoFilter != null) {
- document.filter(this.autoFilter);
- }
- document.filter(this.disabledEndpointsFilter);
- for (OASFilter userFilter : userFilters) {
- document.filter(userFilter);
- }
- document.initialize();
- return document;
+ return builder.build().toYAML().getBytes(StandardCharsets.UTF_8);
}
}
}