Skip to content

Commit

Permalink
Merge pull request #43616 from MikeEdgar/smallrye-open-api-3.13.0-bui…
Browse files Browse the repository at this point in the history
…lder

Bump smallrye-open-api to 3.13.0, MP OpenAPI to 3.1.2
  • Loading branch information
gsmet authored Oct 1, 2024
2 parents 282d218 + dd7c80e commit b073d4f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 67 deletions.
4 changes: 2 additions & 2 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@
<microprofile-rest-client.version>3.0.1</microprofile-rest-client.version>
<microprofile-jwt.version>2.1</microprofile-jwt.version>
<microprofile-lra.version>2.0</microprofile-lra.version>
<microprofile-openapi.version>3.1.1</microprofile-openapi.version>
<microprofile-openapi.version>3.1.2</microprofile-openapi.version>
<smallrye-common.version>2.6.0</smallrye-common.version>
<smallrye-config.version>3.9.1</smallrye-config.version>
<smallrye-health.version>4.1.0</smallrye-health.version>
<smallrye-metrics.version>4.0.0</smallrye-metrics.version>
<smallrye-open-api.version>3.12.0</smallrye-open-api.version>
<smallrye-open-api.version>3.13.0</smallrye-open-api.version>
<smallrye-graphql.version>2.10.0</smallrye-graphql.version>
<smallrye-fault-tolerance.version>6.4.0</smallrye-fault-tolerance.version>
<smallrye-jwt.version>4.6.0</smallrye-jwt.version>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.smallrye.openapi.runtime;

import io.smallrye.openapi.api.SmallRyeOpenAPI;

/**
* Customized {@link SmallRyeOpenAPI.Builder} implementation that only includes
* functionality that should occur at application runtime. Specifically, it only
* supports loading a static OpenAPI file/stream, applying OASFilter instances,
* and writing the OpenAPI model to a DOM for later serialization to JSON or
* YAML.
*/
class OpenAPIRuntimeBuilder extends SmallRyeOpenAPI.Builder {
@Override
public <V, A extends V, O extends V, AB, OB> SmallRyeOpenAPI build() {
var ctx = super.getContext();
buildPrepare(ctx);
buildStaticModel(ctx);
return buildFinalize(ctx);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.quarkus.smallrye.openapi.runtime;

import io.smallrye.openapi.runtime.io.Format;

/**
* Holds instances of the OpenAPI Document
*/
Expand All @@ -11,10 +9,4 @@ public interface OpenApiDocumentHolder {

public byte[] getYamlDocument();

default byte[] getDocument(Format format) {
if (format.equals(Format.JSON)) {
return getJsonDocument();
}
return getYamlDocument();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,63 @@
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

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 io.quarkus.smallrye.openapi.runtime.filter.DisabledRestEndpointsFilter;
import io.smallrye.openapi.api.SmallRyeOpenAPI;
import io.smallrye.openapi.runtime.io.Format;

/**
* Loads the document and make it available
*/
@ApplicationScoped
public class OpenApiDocumentService implements OpenApiDocumentHolder {
public class OpenApiDocumentService {

private final OpenApiDocumentHolder documentHolder;

@Inject
public OpenApiDocumentService(OASFilter autoSecurityFilter,
OpenApiRecorder.UserDefinedRuntimeFilters userDefinedRuntimeFilters, Config config) {
OpenApiRecorder.UserDefinedRuntimeFilters runtimeFilters, Config config) {

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();
Set<String> userFilters = new LinkedHashSet<>(runtimeFilters.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);
SmallRyeOpenAPI.Builder builder = new OpenAPIRuntimeBuilder()
.withConfig(config)
.withApplicationClassLoader(loader)
.enableModelReader(false)
.enableStandardStaticFiles(false)
.enableAnnotationScan(false)
.enableStandardFilter(false)
.withCustomStaticFile(() -> source);

// Auth-security and disabled endpoint filters will only run once
Optional.ofNullable(autoSecurityFilter)
.ifPresent(builder::addFilter);
DisabledRestEndpointsFilter.maybeGetInstance()
.ifPresent(builder::addFilter);

if (dynamic && !userFilters.isEmpty()) {
// Only regenerate the OpenAPI document when configured and there are filters to run
this.documentHolder = new DynamicDocument(builder, loader, userFilters);
} else {
this.documentHolder = new StaticDocument(source, config, autoSecurityFilter, userFilters);
userFilters.forEach(name -> builder.addFilter(name, loader, (IndexView) null));
this.documentHolder = new StaticDocument(builder.build());
}
} else {
this.documentHolder = new EmptyDocument();
Expand All @@ -52,12 +70,11 @@ public OpenApiDocumentService(OASFilter autoSecurityFilter,
}
}

public byte[] getJsonDocument() {
return this.documentHolder.getJsonDocument();
}

public byte[] getYamlDocument() {
return this.documentHolder.getYamlDocument();
byte[] getDocument(Format format) {
if (format.equals(Format.JSON)) {
return documentHolder.getJsonDocument();
}
return documentHolder.getYamlDocument();
}

static class EmptyDocument implements OpenApiDocumentHolder {
Expand All @@ -80,20 +97,7 @@ static class StaticDocument implements OpenApiDocumentHolder {
private byte[] jsonDocument;
private byte[] yamlDocument;

StaticDocument(InputStream source, Config config, OASFilter autoFilter, List<String> 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();
StaticDocument(SmallRyeOpenAPI openAPI) {
jsonDocument = openAPI.toJSON().getBytes(StandardCharsets.UTF_8);
yamlDocument = openAPI.toYAML().getBytes(StandardCharsets.UTF_8);
}
Expand All @@ -108,31 +112,18 @@ public byte[] getYamlDocument() {
}

/**
* Generate the document on every request.
* Generate the document on every request by re-running user-provided OASFilters.
*/
static class DynamicDocument implements OpenApiDocumentHolder {

private SmallRyeOpenAPI.Builder builder;
private OpenAPI generatedOnBuild;

DynamicDocument(InputStream source, Config config, OASFilter autoFilter, List<String> annotatedUserFilters) {
builder = SmallRyeOpenAPI.builder()
.withConfig(config)
.enableModelReader(false)
.enableStandardStaticFiles(false)
.enableAnnotationScan(false)
.enableStandardFilter(false)
.withCustomStaticFile(() -> source);

generatedOnBuild = builder.build().model();

DynamicDocument(SmallRyeOpenAPI.Builder builder, ClassLoader loader, Set<String> userFilters) {
OpenAPI 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);
userFilters.forEach(name -> builder.addFilter(name, loader, (IndexView) null));
this.builder = builder;
}

public byte[] getJsonDocument() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.OpenAPI;
Expand All @@ -20,11 +19,29 @@
*/
public class DisabledRestEndpointsFilter implements OASFilter {

public static Optional<OASFilter> maybeGetInstance() {
var endpoints = DisabledRestEndpoints.get();

if (endpoints != null && !endpoints.isEmpty()) {
return Optional.of(new DisabledRestEndpointsFilter(endpoints));
}

return Optional.empty();
}

final Map<String, List<String>> disabledEndpoints;

private DisabledRestEndpointsFilter(Map<String, List<String>> disabledEndpoints) {
this.disabledEndpoints = disabledEndpoints;
}

@Override
public void filterOpenAPI(OpenAPI openAPI) {
Paths paths = openAPI.getPaths();

disabledRestEndpoints()
disabledEndpoints.entrySet()
.stream()
.map(pathMethods -> Map.entry(stripSlash(pathMethods.getKey()), pathMethods.getValue()))
// Skip paths that are not present in the OpenAPI model
.filter(pathMethods -> paths.hasPathItem(pathMethods.getKey()))
.forEach(pathMethods -> {
Expand All @@ -44,14 +61,6 @@ public void filterOpenAPI(OpenAPI openAPI) {
});
}

static Stream<Map.Entry<String, List<String>>> disabledRestEndpoints() {
return Optional.ofNullable(DisabledRestEndpoints.get())
.orElseGet(Collections::emptyMap)
.entrySet()
.stream()
.map(pathMethods -> Map.entry(stripSlash(pathMethods.getKey()), pathMethods.getValue()));
}

/**
* Removes any trailing slash character from the path when it is not the root '/'
* path. This is necessary to align with the paths generated in the OpenAPI model.
Expand Down

0 comments on commit b073d4f

Please sign in to comment.