diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java index 93d05cd00d4cf..8c1e6cabd5800 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java @@ -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; @@ -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() @@ -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()))) @@ -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")) diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java index af41470e90998..5d40795a50abe 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java @@ -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; @@ -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() @@ -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()))) @@ -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")) diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java index 21010ea098ec4..ab1d6a59245fd 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java @@ -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()) { diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java index 899d511dc502e..de41bdd36fd91 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java @@ -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: diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java index 3d5374aaafcec..7bd2f25f85f48 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java @@ -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()); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java index 9a5504e3fb4af..ad1ebc72adeb1 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java @@ -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"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java index 9346f5d4c7f7c..5180ffa6afc31 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java @@ -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"); @@ -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"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java index 7b6078f238fac..aa74c43a1a369 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java @@ -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; @@ -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); @@ -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("/")); @@ -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); + } + } } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java index 558066874ae5a..e74b77a908a79 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java @@ -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()) {