From fc5ddf53e3c8b13b0c4e09dbf17aa740d968e5cb Mon Sep 17 00:00:00 2001 From: Andrew Rouse Date: Tue, 19 Apr 2022 12:06:56 +0100 Subject: [PATCH] Allow APIResponse on ExceptionMapper class Allow the APIResponse annotation to be used on an exception mapper class as well as the toResponse method. --- .../annotations/responses/APIResponse.java | 6 +-- .../exception/ReviewRejectedException.java | 26 ++++++++++ .../ReviewRejectedExceptionMapper.java | 48 +++++++++++++++++++ .../airlines/resources/ReviewResource.java | 3 +- .../openapi/tck/AirlinesAppTest.java | 8 ++++ 5 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedException.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedExceptionMapper.java diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java index 0beed8592..b47ebcc2e 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java @@ -51,9 +51,9 @@ * specified responseCode the response is not added to that operation. * *

- * When this annotation is applied to an ExceptionMapper, it allows developers to describe the API response - * that will be added to a generated OpenAPI operation based on a JAX-RS method that declares an Exception - * of the type handled by the ExceptionMapper. + * When this annotation is applied to an ExceptionMapper class or toResponse method, it allows + * developers to describe the API response that will be added to a generated OpenAPI operation based on a JAX-RS method + * that declares an Exception of the type handled by the ExceptionMapper. * *

  * @Provider
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedException.java b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedException.java
new file mode 100644
index 000000000..1b38ada1d
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedException.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2022 Contributors to the Eclipse Foundation
+ * 

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.microprofile.openapi.apps.airlines.exception; + +@SuppressWarnings("serial") +public class ReviewRejectedException extends Exception { + + public ReviewRejectedException(String rejectionReason, Throwable cause) { + super(rejectionReason, cause); + } + + public ReviewRejectedException(String rejectionReason) { + super(rejectionReason); + } + +} diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedExceptionMapper.java b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedExceptionMapper.java new file mode 100644 index 000000000..862ebbfe5 --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/exception/ReviewRejectedExceptionMapper.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.microprofile.openapi.apps.airlines.exception; + +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.apps.airlines.exception.ReviewRejectedExceptionMapper.RejectionResponse; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.ext.ExceptionMapper; + +@APIResponse(responseCode = "400", description = "The review was rejected", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = RejectionResponse.class))) +public class ReviewRejectedExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(ReviewRejectedException exception) { + RejectionResponse response = new RejectionResponse(); + response.setReason(exception.getMessage()); + return Response.status(Status.BAD_REQUEST).entity(response).build(); + } + + public static class RejectionResponse { + @Schema(description = "The reason the review was rejected") + private String reason; + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + } + +} diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/resources/ReviewResource.java b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/resources/ReviewResource.java index f084be1b5..7e73f1a03 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/resources/ReviewResource.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/resources/ReviewResource.java @@ -46,6 +46,7 @@ import org.eclipse.microprofile.openapi.annotations.servers.Servers; import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tags; +import org.eclipse.microprofile.openapi.apps.airlines.exception.ReviewRejectedException; import org.eclipse.microprofile.openapi.apps.airlines.model.Airline; import org.eclipse.microprofile.openapi.apps.airlines.model.Review; import org.eclipse.microprofile.openapi.apps.airlines.model.User; @@ -233,7 +234,7 @@ public Response getReviewByAirlineAndUser( @Operation(summary = "Create a Review", operationId = "createReview") @Consumes("application/json") @Produces("application/json") - public Response createReview(Review review) { + public Response createReview(Review review) throws ReviewRejectedException { reviews.put(currentId, review); return Response.status(Status.CREATED).entity("{\"id\":" + currentId++ + "}").build(); } diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java index 7b8d76a84..e4de74007 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java @@ -1029,6 +1029,14 @@ public void testExceptionMappers(String type) { vr.body("paths.'/user/{username}'.get.responses.'404'.description", equalTo("Not Found")); vr.body("paths.'/user/{id}'.get.responses.'404'.content.'application/json'.schema", notNullValue()); + + vr.body("paths.'/reviews'.post.responses.'400'.description", equalTo("The review was rejected")); + vr.body("paths.'/reviews'.post.responses.'400'.content.'application/json'.schema", notNullValue()); + + String rejectedReviewSchema = + dereference(vr, "paths.'/reviews'.post.responses.'400'.content.'application/json'.schema"); + vr.body(rejectedReviewSchema + ".type", equalTo("object")); + vr.body(rejectedReviewSchema + ".properties", hasKey("reason")); } }