Skip to content

Commit

Permalink
Merge pull request quarkusio#28811 from Sgitario/28786
Browse files Browse the repository at this point in the history
Rest Data Panache: Correct Open API integration
  • Loading branch information
geoand authored Oct 25, 2022
2 parents b977abb + cb8bffb commit 84f7bea
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi;

import static org.hamcrest.Matchers.is;

import java.util.List;

import org.hamcrest.Matchers;
Expand All @@ -25,6 +27,7 @@
class OpenApiIntegrationTest {

private static final String OPEN_API_PATH = "/q/openapi";
private static final String COLLECTIONS_SCHEMA_REF = "#/components/schemas/Collection";

@RegisterExtension
static final QuarkusProdModeTest TEST = new QuarkusProdModeTest()
Expand All @@ -36,7 +39,7 @@ class OpenApiIntegrationTest {
.addAsResource("application.properties")
.addAsResource("import.sql"))
.setForcedDependencies(List.of(
Dependency.of("io.quarkus", "quarkus-smallrye-openapi", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-smallrye-openapi-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-jdbc-h2-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-resteasy-jsonb-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-security-deployment", Version.getVersion())))
Expand All @@ -51,14 +54,28 @@ public void testOpenApiForGeneratedResources() {
.body("info.title", Matchers.equalTo("quarkus-hibernate-orm-rest-data-panache-deployment API"))
.body("paths.'/collections'", Matchers.hasKey("get"))
.body("paths.'/collections'.get.tags", Matchers.hasItem("CollectionsResource"))
.body("paths.'/collections'.get.responses.'200'.content.'application/json'.schema.type", is("array"))
.body("paths.'/collections'.get.responses.'200'.content.'application/json'.schema.items.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'", Matchers.hasKey("post"))
.body("paths.'/collections'.post.tags", Matchers.hasItem("CollectionsResource"))
.body("paths.'/collections'.post.requestBody.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'.post.responses.'201'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'.post.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("get"))
.body("paths.'/collections/{id}'.get.responses.'200'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.get.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("put"))
.body("paths.'/collections/{id}'.put.requestBody.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.put.responses.'201'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.put.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("delete"))
.body("paths.'/collections/{id}'.delete.responses", Matchers.hasKey("204"))
.body("paths.'/collections/{id}'.delete.security[0].SecurityScheme", Matchers.hasItem("admin"))
.body("paths.'/empty-list-items'", Matchers.hasKey("get"))
.body("paths.'/empty-list-items'.get.tags", Matchers.hasItem("EmptyListItemsResource"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.openapi;

import static org.hamcrest.Matchers.is;

import java.util.List;

import org.hamcrest.Matchers;
Expand All @@ -25,6 +27,7 @@
class OpenApiIntegrationTest {

private static final String OPEN_API_PATH = "/q/openapi";
private static final String COLLECTIONS_SCHEMA_REF = "#/components/schemas/Collection";

@RegisterExtension
static final QuarkusProdModeTest TEST = new QuarkusProdModeTest()
Expand All @@ -36,7 +39,7 @@ class OpenApiIntegrationTest {
.addAsResource("application.properties")
.addAsResource("import.sql"))
.setForcedDependencies(List.of(
Dependency.of("io.quarkus", "quarkus-smallrye-openapi", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-smallrye-openapi-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-reactive-pg-client-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-resteasy-reactive-jsonb-deployment", Version.getVersion()),
Dependency.of("io.quarkus", "quarkus-security-deployment", Version.getVersion())))
Expand All @@ -51,14 +54,28 @@ public void testOpenApiForGeneratedResources() {
.body("info.title", Matchers.equalTo("quarkus-hibernate-reactive-rest-data-panache-deployment API"))
.body("paths.'/collections'", Matchers.hasKey("get"))
.body("paths.'/collections'.get.tags", Matchers.hasItem("CollectionsResource"))
.body("paths.'/collections'.get.responses.'200'.content.'application/json'.schema.type", is("array"))
.body("paths.'/collections'.get.responses.'200'.content.'application/json'.schema.items.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'", Matchers.hasKey("post"))
.body("paths.'/collections'.post.tags", Matchers.hasItem("CollectionsResource"))
.body("paths.'/collections'.post.requestBody.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'.post.responses.'201'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections'.post.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("get"))
.body("paths.'/collections/{id}'.get.responses.'200'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.get.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("put"))
.body("paths.'/collections/{id}'.put.requestBody.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.put.responses.'201'.content.'application/json'.schema.$ref",
is(COLLECTIONS_SCHEMA_REF))
.body("paths.'/collections/{id}'.put.security[0].SecurityScheme", Matchers.hasItem("user"))
.body("paths.'/collections/{id}'", Matchers.hasKey("delete"))
.body("paths.'/collections/{id}'.delete.responses", Matchers.hasKey("204"))
.body("paths.'/collections/{id}'.delete.security[0].SecurityScheme", Matchers.hasItem("admin"))
.body("paths.'/empty-list-items'", Matchers.hasKey("get"))
.body("paths.'/empty-list-items'.get.tags", Matchers.hasItem("EmptyListItemsResource"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
addConsumesAnnotation(methodCreator, APPLICATION_JSON);
addProducesJsonAnnotation(methodCreator, resourceProperties);
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
addOpenApiResponseAnnotation(methodCreator, Response.Status.CREATED, resourceMetadata.getEntityType());
addSecurityAnnotations(methodCreator, resourceProperties);
// Add parameter annotations
if (hasValidatorCapability()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
addGetAnnotation(methodCreator);
addProducesAnnotation(methodCreator, APPLICATION_JSON);
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), RESOURCE_METHOD_NAME));
addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, Long.class, false);
addSecurityAnnotations(methodCreator, resourceProperties);
if (!isResteasyClassic()) {
// We only add the Links annotation in Resteasy Reactive because Resteasy Classic ignores the REL parameter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
addDeleteAnnotation(methodCreator);
addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
addOpenApiResponseAnnotation(methodCreator, Response.Status.NO_CONTENT);
addSecurityAnnotations(methodCreator, resourceProperties);

ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}"));
addGetAnnotation(methodCreator);
addProducesJsonAnnotation(methodCreator, resourceProperties);
addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType());
addSecurityAnnotations(methodCreator, resourceProperties);

addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource
addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
addProducesAnnotation(methodCreator, APPLICATION_JSON);
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType(), true);
addSecurityAnnotations(methodCreator, resourceProperties);
addSortQueryParamValidatorAnnotation(methodCreator);
addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort");
Expand Down Expand Up @@ -209,6 +210,7 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou
addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
addProducesAnnotation(methodCreator, APPLICATION_JSON);
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType(), true);
addSecurityAnnotations(methodCreator, resourceProperties);
addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.jboss.logging.Logger;

Expand All @@ -33,7 +34,11 @@
* A standard JAX-RS method implementor.
*/
public abstract class StandardMethodImplementor implements MethodImplementor {

private static final String OPENAPI_PACKAGE = "org.eclipse.microprofile.openapi.annotations";
private static final String OPENAPI_RESPONSE_ANNOTATION = OPENAPI_PACKAGE + ".responses.APIResponse";
private static final String OPENAPI_CONTENT_ANNOTATION = OPENAPI_PACKAGE + ".media.Content";
private static final String OPENAPI_SCHEMA_ANNOTATION = OPENAPI_PACKAGE + ".media.Schema";
private static final String SCHEMA_TYPE_ARRAY = "ARRAY";
private static final String ROLES_ALLOWED_ANNOTATION = "javax.annotation.security.RolesAllowed";
private static final Logger LOGGER = Logger.getLogger(StandardMethodImplementor.class);

Expand Down Expand Up @@ -156,6 +161,43 @@ protected void addSecurityAnnotations(AnnotatedElement element, ResourceProperti
}
}

protected void addOpenApiResponseAnnotation(AnnotatedElement element, Response.Status status) {
if (capabilities.isPresent(Capability.SMALLRYE_OPENAPI)) {
element.addAnnotation(OPENAPI_RESPONSE_ANNOTATION)
.add("responseCode", String.valueOf(status.getStatusCode()));
}
}

protected void addOpenApiResponseAnnotation(AnnotatedElement element, Response.Status status, String entityType) {
addOpenApiResponseAnnotation(element, status, entityType, false);
}

protected void addOpenApiResponseAnnotation(AnnotatedElement element, Response.Status status, String entityType,
boolean isList) {
if (capabilities.isPresent(Capability.SMALLRYE_OPENAPI)) {
addOpenApiResponseAnnotation(element, status, toClass(entityType), isList);
}
}

protected void addOpenApiResponseAnnotation(AnnotatedElement element, Response.Status status, Class<?> clazz,
boolean isList) {
if (capabilities.isPresent(Capability.SMALLRYE_OPENAPI)) {
AnnotationCreator schemaAnnotation = AnnotationCreator.of(OPENAPI_SCHEMA_ANNOTATION)
.add("implementation", clazz);

if (isList) {
schemaAnnotation.add("type", SCHEMA_TYPE_ARRAY);
}

element.addAnnotation(OPENAPI_RESPONSE_ANNOTATION)
.add("responseCode", String.valueOf(status.getStatusCode()))
.add("content", new Object[] { AnnotationCreator.of(OPENAPI_CONTENT_ANNOTATION)
.add("mediaType", APPLICATION_JSON)
.add("schema", schemaAnnotation)
});
}
}

protected String appendToPath(String path, String suffix) {
if (path.endsWith("/")) {
path = path.substring(0, path.lastIndexOf("/"));
Expand All @@ -181,4 +223,13 @@ protected boolean isResteasyClassic() {
protected boolean isNotReactivePanache() {
return !capabilities.isPresent(Capability.HIBERNATE_REACTIVE);
}

private static Class<?> toClass(String className) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
return classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("The class (" + className + ") cannot be found during deployment.", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
addConsumesAnnotation(methodCreator, APPLICATION_JSON);
addProducesJsonAnnotation(methodCreator, resourceProperties);
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
addOpenApiResponseAnnotation(methodCreator, Response.Status.CREATED, resourceMetadata.getEntityType());
addSecurityAnnotations(methodCreator, resourceProperties);
// Add parameter annotations
if (hasValidatorCapability()) {
Expand Down

0 comments on commit 84f7bea

Please sign in to comment.