From 43085791e336b19e37a71b7d7daffa3f6eae2c4d Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 8 Aug 2023 00:49:01 +1200 Subject: [PATCH] Add HttpStatus enum for status code --- .../io/avaje/http/api/ExceptionHandler.java | 2 +- .../java/io/avaje/http/api/HttpStatus.java | 84 +++++++++++++++++++ .../main/java/io/avaje/http/api/Produces.java | 2 +- .../http/generator/core/CopyHttpStatus.java | 83 ++++++++++++++++++ .../http/generator/core/MethodReader.java | 16 ++-- .../myapp/web/test/OpenAPIController.java | 12 +-- .../java/org/example/ErrorController.java | 3 +- .../main/java/org/example/TestController.java | 4 +- 8 files changed, 186 insertions(+), 20 deletions(-) create mode 100644 http-api/src/main/java/io/avaje/http/api/HttpStatus.java create mode 100644 http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java diff --git a/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java b/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java index a9de0e1b3..1120cd48e 100644 --- a/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java +++ b/http-api/src/main/java/io/avaje/http/api/ExceptionHandler.java @@ -41,5 +41,5 @@ /** * The response status code to use. */ - int statusCode() default 0; + HttpStatus statusCode() default HttpStatus.__not_set; } diff --git a/http-api/src/main/java/io/avaje/http/api/HttpStatus.java b/http-api/src/main/java/io/avaje/http/api/HttpStatus.java new file mode 100644 index 000000000..6591fd99a --- /dev/null +++ b/http-api/src/main/java/io/avaje/http/api/HttpStatus.java @@ -0,0 +1,84 @@ +package io.avaje.http.api; + +public enum HttpStatus { + CONTINUE_100(100), + SWITCHING_PROTOCOLS_101(101), + PROCESSING_102(102), + + OK_200(200), + CREATED_201(201), + ACCEPTED_202(202), + NON_AUTHORITATIVE_INFORMATION_203(203), + + NO_CONTENT_204(204), + RESET_CONTENT_205(205), + PARTIAL_CONTENT_206(206), + MULTI_STATUS_207(207), + ALREADY_REPORTED_208(208), + IM_USED_226(226), + + MULTIPLE_CHOICES_300(300), + MOVED_PERMANENTLY_301(301), + FOUND_302(302), + SEE_OTHER_303(303), + NOT_MODIFIED_304(304), + USE_PROXY_305(305), + TEMPORARY_REDIRECT_307(307), + PERMANENT_REDIRECT_308(308), + + BAD_REQUEST_400(400), + UNAUTHORIZED_401(401), + PAYMENT_REQUIRED_402(402), + FORBIDDEN_403(403), + NOT_FOUND_404(404), + METHOD_NOT_ALLOWED_405(405), + NOT_ACCEPTABLE_406(406), + PROXY_AUTHENTICATION_REQUIRED_407(407), + REQUEST_TIMEOUT_408(408), + CONFLICT_409(409), + GONE_410(410), + LENGTH_REQUIRED_411(411), + PRECONDITION_FAILED_412(412), + REQUEST_ENTITY_TOO_LARGE_413(413), + URI_TOO_LONG_414(414), + UNSUPPORTED_MEDIA_TYPE_415(415), + REQUESTED_RANGE_NOT_SATISFIABLE_416(416), + EXPECTATION_FAILED_417(417), + I_AM_A_TEAPOT_418(418), + MISDIRECTED_REQUEST_421(421), + UNPROCESSABLE_ENTITY_422(422), + LOCKED_423(423), + FAILED_DEPENDENCY_424(424), + TOO_EARLY_425(425), + UPGRADE_REQUIRED_426(426), + PRECONDITION_REQUIRED_428(428), + TOO_MANY_REQUESTS_429(429), + REQUEST_HEADER_FIELDS_TOO_LARGE_431(431), + UNAVAILABLE_FOR_LEGAL_REASONS_451(451), + + INTERNAL_SERVER_ERROR_500(500), + NOT_IMPLEMENTED_501(501), + BAD_GATEWAY_502(502), + SERVICE_UNAVAILABLE_503(503), + GATEWAY_TIMEOUT_504(504), + HTTP_VERSION_NOT_SUPPORTED_505(505), + VARIANT_ALSO_NEGOTIATES_506(506), + INSUFFICIENT_STORAGE(507), + LOOP_DETECTED(508), + BANDWIDTH_LIMIT_EXCEEDED_509(509), + NOT_EXTENDED_510(510), + NETWORK_AUTHENTICATION_REQUIRED(511), + + __not_set(0); + + private final int code; + + HttpStatus(int code) { + this.code = code; + } + + public int code() { + return code; + } + +} diff --git a/http-api/src/main/java/io/avaje/http/api/Produces.java b/http-api/src/main/java/io/avaje/http/api/Produces.java index 09b4db9a6..6055579df 100644 --- a/http-api/src/main/java/io/avaje/http/api/Produces.java +++ b/http-api/src/main/java/io/avaje/http/api/Produces.java @@ -45,5 +45,5 @@ * PATCH(200, void methods 204)
* DELETE(200, void methods 204) */ - int statusCode() default 0; + HttpStatus statusCode() default HttpStatus.__not_set; } diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java new file mode 100644 index 000000000..0f64549a5 --- /dev/null +++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/CopyHttpStatus.java @@ -0,0 +1,83 @@ +package io.avaje.http.generator.core; + +public enum CopyHttpStatus { + CONTINUE_100(100), + SWITCHING_PROTOCOLS_101(101), + PROCESSING_102(102), + + OK_200(200), + CREATED_201(201), + ACCEPTED_202(202), + NON_AUTHORITATIVE_INFORMATION_203(203), + + NO_CONTENT_204(204), + RESET_CONTENT_205(205), + PARTIAL_CONTENT_206(206), + MULTI_STATUS_207(207), + ALREADY_REPORTED_208(208), + IM_USED_226(226), + + MULTIPLE_CHOICES_300(300), + MOVED_PERMANENTLY_301(301), + FOUND_302(302), + SEE_OTHER_303(303), + NOT_MODIFIED_304(304), + USE_PROXY_305(305), + TEMPORARY_REDIRECT_307(307), + PERMANENT_REDIRECT_308(308), + + BAD_REQUEST_400(400), + UNAUTHORIZED_401(401), + PAYMENT_REQUIRED_402(402), + FORBIDDEN_403(403), + NOT_FOUND_404(404), + METHOD_NOT_ALLOWED_405(405), + NOT_ACCEPTABLE_406(406), + PROXY_AUTHENTICATION_REQUIRED_407(407), + REQUEST_TIMEOUT_408(408), + CONFLICT_409(409), + GONE_410(410), + LENGTH_REQUIRED_411(411), + PRECONDITION_FAILED_412(412), + REQUEST_ENTITY_TOO_LARGE_413(413), + URI_TOO_LONG_414(414), + UNSUPPORTED_MEDIA_TYPE_415(415), + REQUESTED_RANGE_NOT_SATISFIABLE_416(416), + EXPECTATION_FAILED_417(417), + I_AM_A_TEAPOT_418(418), + MISDIRECTED_REQUEST_421(421), + UNPROCESSABLE_ENTITY_422(422), + LOCKED_423(423), + FAILED_DEPENDENCY_424(424), + TOO_EARLY_425(425), + UPGRADE_REQUIRED_426(426), + PRECONDITION_REQUIRED_428(428), + TOO_MANY_REQUESTS_429(429), + REQUEST_HEADER_FIELDS_TOO_LARGE_431(431), + UNAVAILABLE_FOR_LEGAL_REASONS_451(451), + + INTERNAL_SERVER_ERROR_500(500), + NOT_IMPLEMENTED_501(501), + BAD_GATEWAY_502(502), + SERVICE_UNAVAILABLE_503(503), + GATEWAY_TIMEOUT_504(504), + HTTP_VERSION_NOT_SUPPORTED_505(505), + VARIANT_ALSO_NEGOTIATES_506(506), + INSUFFICIENT_STORAGE(507), + LOOP_DETECTED(508), + BANDWIDTH_LIMIT_EXCEEDED_509(509), + NOT_EXTENDED_510(510), + NETWORK_AUTHENTICATION_REQUIRED(511), + + __not_set(0); + + private final int code; + + CopyHttpStatus(int code) { + this.code = code; + } + + public int code() { + return code; + } +} diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java index 26ae68fc3..7f049336c 100644 --- a/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java +++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java @@ -48,7 +48,7 @@ public class MethodReader { private final Optional timeout; private WebMethod webMethod; - private int statusCode; + private CopyHttpStatus statusCode = CopyHttpStatus.__not_set; private String webMethodPath; private boolean formMarker; private final boolean instrumentContext; @@ -169,7 +169,7 @@ private void initSetWebMethod(WebMethod webMethod, String value) { private void initSetWebMethod(WebMethod webMethod, ExceptionHandlerPrism exceptionPrism) { this.webMethod = webMethod; - this.statusCode = exceptionPrism.statusCode(); + this.statusCode = CopyHttpStatus.valueOf(exceptionPrism.statusCode()); var exType = exceptionPrism.value().toString(); if ("io.avaje.http.api.DefaultException".equals(exType)) { exType = @@ -352,7 +352,11 @@ public boolean isVoid() { } public boolean hasProducesStatus() { - return producesAnnotation.map(ProducesPrism::statusCode).filter(s -> s > 0).isPresent(); + return producesAnnotation + .map(ProducesPrism::statusCode) + .map(CopyHttpStatus::valueOf) + .filter(s -> s.code() > 0) + .isPresent(); } public String produces() { @@ -379,12 +383,14 @@ public TypeMirror returnType() { } public int statusCode() { - if (statusCode > 0) { + if (statusCode.code() > 0) { // using explicit status code - return statusCode; + return statusCode.code(); } return producesAnnotation .map(ProducesPrism::statusCode) + .map(CopyHttpStatus::valueOf) + .map(CopyHttpStatus::code) .filter(s -> s > 0) .orElseGet(() -> webMethod.statusCode(isVoid)); } diff --git a/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java b/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java index 709e76b43..b9a0ff8f7 100644 --- a/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java +++ b/tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/OpenAPIController.java @@ -2,15 +2,7 @@ import java.util.List; -import io.avaje.http.api.Controller; -import io.avaje.http.api.Get; -import io.avaje.http.api.MediaType; -import io.avaje.http.api.OpenAPIResponse; -import io.avaje.http.api.OpenAPIResponses; -import io.avaje.http.api.Path; -import io.avaje.http.api.Post; -import io.avaje.http.api.Produces; -import io.avaje.http.api.Put; +import io.avaje.http.api.*; import io.javalin.http.Context; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; @@ -84,7 +76,7 @@ Person testPostList(List m) { } @Put("/put") - @Produces(value = MediaType.TEXT_PLAIN, statusCode = 203) + @Produces(value = MediaType.TEXT_PLAIN, statusCode = HttpStatus.NON_AUTHORITATIVE_INFORMATION_203) @OpenAPIResponse(responseCode = "204", type = String.class) String testDefaultStatus(Context ctx) { if (ctx.contentType().equals(MediaType.APPLICATION_PDF)) { diff --git a/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java b/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java index bf841810b..37ca51616 100644 --- a/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java +++ b/tests/test-nima-jsonb/src/main/java/org/example/ErrorController.java @@ -2,6 +2,7 @@ import io.avaje.http.api.Controller; import io.avaje.http.api.ExceptionHandler; +import io.avaje.http.api.HttpStatus; import io.helidon.nima.webserver.http.ServerRequest; import io.helidon.nima.webserver.http.ServerResponse; @@ -13,7 +14,7 @@ @Controller final class ErrorController { - @ExceptionHandler(statusCode = 407) + @ExceptionHandler(statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407) Map runEx(RuntimeException ex, ServerRequest req, ServerResponse res) { return Map.of("err", "" + ex); } diff --git a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java index 6b2412921..c17daf6a0 100644 --- a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java +++ b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java @@ -69,7 +69,7 @@ String strBody(@BodyString String body) { return body; } - @Produces(statusCode = 202) + @Produces(statusCode = HttpStatus.ACCEPTED_202) @Post("/blah") Map strBody2() { var map = new LinkedHashMap(); @@ -83,7 +83,7 @@ String exception(IllegalArgumentException ex) { return "Err: " + ex; } - @Produces(statusCode = 501) + @Produces(statusCode = HttpStatus.NOT_IMPLEMENTED_501) @ExceptionHandler Person exceptionCtx(Exception ex, ServerRequest req, ServerResponse res) { res.header("X-Foo", "WasHere");