From 824df210f107f14e877ceaea7192b87bc505e4bc Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Sun, 22 Nov 2020 23:27:50 +0330 Subject: [PATCH 1/6] Supporting spring boot 2.4.0 --- README.md | 6 +++--- pom.xml | 12 +++++++++--- .../adapter/attributes/ReactiveErrorAttributes.java | 10 +++++----- .../adapter/attributes/ServletErrorAttributes.java | 10 ++++++---- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 290c92f..f76a441 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ or Gradle: compile "me.alidg:errors-spring-boot-starter:1.4.0" ``` -If you like to stay at the cutting edge, use our `1.5.0-SNAPSHOT` version. Of course you should define the following +If you like to stay at the cutting edge, use our `1.6.0-SNAPSHOT` version. Of course, you should define the following snapshot repository: ```xml @@ -99,7 +99,7 @@ repositories { ### Prerequisites The main dependency is JDK 8+. Tested with: - JDK 8, JDK 9, JDK 10 and JDK 11 on Linux. - - Spring Boot `2.2.0.RELEASE` (Also, should work with any `2.0.0+`) + - Spring Boot `2.4.0` (Also, should work with any `2.3.0+`) ### Overview The `WebErrorHandler` implementations are responsible for handling different kinds of exceptions. When an exception @@ -167,7 +167,7 @@ key-value pair in our message resource file: ```properties user.already_exists=Another user with the same username already exists ``` -Then if an exception of type `UserAlreadyExistsException` was thrown, you would see a `400 Bad Request` HTTP response +Then if an exception to type `UserAlreadyExistsException` was thrown, you would see a `400 Bad Request` HTTP response with a body like: ```json { diff --git a/pom.xml b/pom.xml index 9aedd1f..3bfc950 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 4.0.0 me.alidg errors-spring-boot-starter - 1.5.0-SNAPSHOT + 1.6.0-SNAPSHOT ${project.groupId}:${project.artifactId} @@ -62,7 +62,7 @@ UTF-8 - 2.2.0.RELEASE + 2.4.0 1.8 1.1.1 0.8.5 @@ -73,7 +73,7 @@ 3.0.2 3.0.1 1.6 - 5.2.0.RELEASE + 5.4.1 @@ -89,6 +89,12 @@ ${spring-boot.version} true + + org.springframework.boot + spring-boot-starter-validation + ${spring-boot.version} + true + org.springframework.boot spring-boot-starter-security diff --git a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java index 6b37b1a..f311c31 100644 --- a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java +++ b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java @@ -3,6 +3,7 @@ import me.alidg.errors.HttpError; import me.alidg.errors.WebErrorHandlers; import me.alidg.errors.adapter.HttpErrorAttributesAdapter; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; @@ -42,7 +43,6 @@ public class ReactiveErrorAttributes extends DefaultErrorAttributes { */ public ReactiveErrorAttributes(WebErrorHandlers webErrorHandlers, HttpErrorAttributesAdapter httpErrorAttributesAdapter) { - super(true); this.webErrorHandlers = requireNonNull(webErrorHandlers, "Web error handlers is required"); this.httpErrorAttributesAdapter = requireNonNull(httpErrorAttributesAdapter, "Adapter is required"); } @@ -51,13 +51,13 @@ public ReactiveErrorAttributes(WebErrorHandlers webErrorHandlers, * Handles the exception by delegating it to the {@link #webErrorHandlers} and then adapting * the representation. * - * @param request The source request. - * @param includeStackTrace whether to include the error stacktrace information. + * @param request The source request. + * @param options whether to include the error stacktrace information. * @return A map of error attributes */ @Override - public Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) { - Map attributes = super.getErrorAttributes(request, includeStackTrace); + public Map getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { + Map attributes = super.getErrorAttributes(request, options); Throwable exception = getError(request); if (exception == null || isNotFoundException(exception)) exception = Exceptions.refineUnknownException(attributes); diff --git a/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java b/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java index cb5e6b1..e5d7777 100644 --- a/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java +++ b/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java @@ -3,6 +3,7 @@ import me.alidg.errors.HttpError; import me.alidg.errors.WebErrorHandlers; import me.alidg.errors.adapter.HttpErrorAttributesAdapter; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.web.context.request.WebRequest; @@ -61,13 +62,14 @@ public ServletErrorAttributes(WebErrorHandlers webErrorHandlers, * the exception using the {@link #webErrorHandlers} and adapts the returned {@link HttpError} to a * Spring Boot compatible representation. * - * @param webRequest The current HTTP request. - * @param includeStackTrace Whether or not to include the stack trace in the error attributes. + * @param webRequest The current HTTP request. + * @param options Whether or not to include the stack trace in the error attributes. * @return Error details. */ @Override - public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { - Map attributes = super.getErrorAttributes(webRequest, includeStackTrace); + public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { + Map attributes = super.getErrorAttributes(webRequest, options); + Throwable exception = getError(webRequest); if (exception == null) exception = Exceptions.refineUnknownException(attributes); From 12d3db31400b21e5782d9f0d5b2475932482c5c3 Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Wed, 6 Dec 2023 20:42:24 +0330 Subject: [PATCH 2/6] Supporting spring boot 3.0.0+ --- .travis.yml | 6 +- README.md | 27 +++--- pom.xml | 32 +++++-- .../java/me/alidg/errors/ExceptionLogger.java | 3 +- .../me/alidg/errors/FingerprintProvider.java | 9 +- src/main/java/me/alidg/errors/HttpError.java | 3 +- .../errors/WebErrorHandlerPostProcessor.java | 11 +-- .../me/alidg/errors/WebErrorHandlers.java | 30 +------ .../alidg/errors/WebErrorHandlersBuilder.java | 14 +-- .../errors/adapter/attributes/Exceptions.java | 18 ++-- .../attributes/ReactiveErrorAttributes.java | 4 +- .../attributes/ServletErrorAttributes.java | 4 +- .../annotation/AutoConfigureErrors.java | 30 ------- .../errors/conf/ErrorsAutoConfiguration.java | 8 +- .../alidg/errors/conf/ErrorsProperties.java | 2 +- ...activeSecurityErrorsAutoConfiguration.java | 2 +- .../conf/ServletErrorsAutoConfiguration.java | 14 +-- ...ervletSecurityErrorsAutoConfiguration.java | 10 +-- .../handlers/AnnotatedWebErrorHandler.java | 8 +- .../ConstraintViolationWebErrorHandler.java | 12 +-- .../errors/handlers/ConstraintViolations.java | 13 ++- ...ssingRequestParametersWebErrorHandler.java | 9 +- .../handlers/MultipartWebErrorHandler.java | 4 +- .../ResponseStatusWebErrorHandler.java | 37 ++++---- .../handlers/ServletWebErrorHandler.java | 37 +++++--- .../SpringValidationWebErrorHandler.java | 4 +- .../handlers/TypeMismatchWebErrorHandler.java | 4 +- .../alidg/errors/message/TemplateParser.java | 8 +- .../errors/mvc/ErrorsControllerAdvice.java | 4 +- src/main/resources/META-INF/spring.factories | 12 --- ...ot.autoconfigure.AutoConfiguration.imports | 5 ++ .../me/alidg/errors/WebErrorHandlersIT.java | 16 ++-- .../me/alidg/errors/WebErrorHandlersTest.java | 43 ---------- .../attributes/ServletErrorAttributesIT.java | 8 +- .../annotation/AutoConfigureErrorsIT.java | 86 ------------------- ...onstraintViolationWebErrorHandlerTest.java | 16 ++-- .../ResponseStatusWebErrorHandlerTest.java | 19 +--- .../handlers/ServletWebErrorHandlerTest.java | 2 +- .../SpringValidationWebErrorHandlerTest.java | 12 +-- .../mvc/ErrorsControllerAdviceTest.java | 30 +++---- .../alidg/errors/reactive/ReactiveConfig.java | 20 ++--- .../errors/reactive/ReactiveController.java | 17 ++-- .../me/alidg/errors/reactive/ReactiveIT.java | 41 +++++---- .../alidg/errors/servlet/ServletConfig.java | 26 +++--- .../errors/servlet/ServletController.java | 22 ++--- .../me/alidg/errors/servlet/ServletIT.java | 14 +-- src/test/resources/logback-test.xml | 5 -- 47 files changed, 282 insertions(+), 479 deletions(-) delete mode 100644 src/main/java/me/alidg/errors/annotation/AutoConfigureErrors.java delete mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 src/test/java/me/alidg/errors/WebErrorHandlersTest.java delete mode 100644 src/test/java/me/alidg/errors/annotation/AutoConfigureErrorsIT.java delete mode 100644 src/test/resources/logback-test.xml diff --git a/.travis.yml b/.travis.yml index 5980184..3d9bf0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,6 @@ addons: secure: "Yd79/X/4ynYpXYNS0npZQ/xcNZTzfl4XxIQQ0Ajf8SiUBJlayvSgFMQ1Kz0iph0/1l2iEV9lDhk7zEd0ZB5C6VKJlBjcpCU50XdIxP4byZ9SKLYR0dqBB0p6QHTSZ+nJjR4wszQzgwu/q/b30L29q3b++cQbbIuIxkVkJnqXptxVAP8uPV+lKLpMORWdrXOA9HQ+Q8Rar/FtBGnVjWVRvkXc168O6V3tV5eygKTyBpE6IBwjVnWxgKILe3zUasCjQTUTkMevHNTEpX/8RlO9JEwBaWMZrrRBhHmUjXSRNLSAkXDQRoOgQsXsv342EwA6RVwxGo2bqIIxJe+zKwfWcMMh24O0QZtYObbfmrGvemKv/FaZ/IhlxG9GntOI1VBjqh4xOdQR8IfPNnSP7iMTj2Q/oKZValRFAEI7Gn6b70mAIPfkktXAv1+AdoU33AVb5UZAtmhgiTylcZMdFstdhXxEgY85ei8q1SsM9718KEhys/azlOeVkNOuACmTAjp61umE32NITlS3eHpkOXM+R7H0AtCWymfmrtJ9jETg1lYArMUO6PdQUtumKm/DfY+nYUoQYdbNFPwqVZl22VK/ploDw9xzX80btd3YHtQkQyyesG5S/3XcxuYJGqkP4Qzs1iV518lA9uVCcGV4DG0Tg4nKc2eG3NqI3q08sPZdNSs=" matrix: include: - - jdk: openjdk8 - os: linux - env: JAVA_VERSION=1.8 - jdk: openjdk9 os: linux env: JAVA_VERSION=9 @@ -18,6 +15,9 @@ matrix: - jdk: openjdk11 os: linux env: JAVA_VERSION=11 + - jdk: openjdk17 + os: linux + env: JAVA_VERSION=17 install: true script: - ./mvnw clean verify sonar:sonar -Dsonar.projectKey=alimate_errors-spring-boot-starter -B diff --git a/README.md b/README.md index f76a441..901e8bd 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/alimate_errors-spring-boot-starter?label=code%20quality&server=https%3A%2F%2Fsonarcloud.io)](https://sonarcloud.io/dashboard?id=alimate_errors-spring-boot-starter) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -

A Bootiful, Consistent and Opinionated Approach to Handle all sorts of Exceptions.

+

A Bountiful, Consistent and Opinionated Approach to Handle all sorts of Exceptions.

## Table of Contents @@ -50,7 +50,7 @@ Built on top of Spring Boot's great exception handling mechanism, the `errors-sp - A consistent approach to handle all exceptions. Doesn't matter if it's a validation/binding error or a custom domain-specific error or even a Spring related error, All of them would be handled by a `WebErrorHandler` implementation (No more `ErrorController` vs `@ExceptionHandler` vs `WebExceptionHandler`) - - Built-in support for application specific error codes, again, for all possible errors. + - Built-in support for application-specific error codes, again, for all possible errors. - Simple error message interpolation using plain old `MessageSource`s. - Customizable HTTP error representation. - Exposing arguments from exceptions to error messages. @@ -77,7 +77,7 @@ or Gradle: compile "me.alidg:errors-spring-boot-starter:1.4.0" ``` -If you like to stay at the cutting edge, use our `1.6.0-SNAPSHOT` version. Of course, you should define the following +If you like to stay at the cutting edge, use our `2.0.0-SNAPSHOT` version. Of course, you should define the following snapshot repository: ```xml @@ -98,8 +98,8 @@ repositories { ### Prerequisites The main dependency is JDK 8+. Tested with: - - JDK 8, JDK 9, JDK 10 and JDK 11 on Linux. - - Spring Boot `2.4.0` (Also, should work with any `2.3.0+`) + - JDK 9, JDK 10, and JDK 11 on Linux. + - Spring Boot `3.0.0+` ### Overview The `WebErrorHandler` implementations are responsible for handling different kinds of exceptions. When an exception @@ -161,8 +161,10 @@ In `errors-spring-boot-starter`, one can map exceptions to error codes in differ ``` ### Error Message -Once the exception mapped to error code(s), we can add a companion and *Human Readable* error message. This can be done -by registering a Spring `MessageSource` to perform the *code-to-message* translation. For example, if we add the following +Once the exception is mapped to error code(s), we can add a companion and *Human Readable* error message. +This can be done +by registering a Spring `MessageSource` to perform the *code-to-message* translation. +For example, if we add the following key-value pair in our message resource file: ```properties user.already_exists=Another user with the same username already exists @@ -223,7 +225,8 @@ Then the `username` property from the `UserAlreadyExistsException` would be avai return type. The `HandledException` class also accepts the *to-be-exposed* arguments in its constructor. #### Exposing Named Arguments -By default error arguments will be used in message interpolation only. It is also possible to additionally get those +By default, error arguments will be used in message interpolation only. +It is also possible to additionally get those arguments in error response by defining the configuration property `errors.expose-arguments`. When enabled, you might get the following response payload: ```json @@ -240,7 +243,7 @@ When enabled, you might get the following response payload: } ``` -The `errors.expose-arguments` property takes 3 possible values: +The `errors.expose-arguments` property takes three possible values: - `NEVER` - named arguments will never be exposed. This is the default setting. - `NON_EMPTY` - named arguments will be exposed only in case there are any. If error has no arguments, result payload will not have `"arguments"` element. @@ -514,7 +517,7 @@ public class CustomExceptionRefiner implements ExceptionRefiner { ### Logging Exceptions By default, the starter issues a few `debug` logs under the `me.alidg.errors.WebErrorHandlers` logger name. -In order to customize the way we log exceptions, we just need to implement the `ExceptionLogger` interface and register it +To customize the way we log exceptions, we just need to implement the `ExceptionLogger` interface and register it as a *Spring Bean*: ```java @Component @@ -555,7 +558,7 @@ public class LoggingErrorWebErrorHandlerPostProcessor implements WebErrorHandler ``` ### Registering Custom Handlers -In order to provide a custom handler for a specific exception, just implement the `WebErrorHandler` interface for that +To provide a custom handler for a specific exception, just implement the `WebErrorHandler` interface for that exception and register it as a *Spring Bean*: ```java @Component @@ -579,7 +582,7 @@ handlers would be registered after built-in exception handlers (Validation, `Exc this idea, provide a custom *Bean* of type `WebErrorHandlers` and the default one would be discarded. ### Test Support -In order to enable our test support for `WebMvcTest`s, just add the `@AutoConfigureErrors` annotation to your test +To enable our test support for `WebMvcTest`s, just add the `@AutoConfigureErrors` annotation to your test class. That's how a `WebMvcTest` would look like with errors support enabled: ```java @AutoConfigureErrors diff --git a/pom.xml b/pom.xml index 3bfc950..a251442 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 4.0.0 me.alidg errors-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-SNAPSHOT ${project.groupId}:${project.artifactId} @@ -19,7 +19,7 @@ The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt @@ -41,6 +41,11 @@ Mona Mohamadinia mona.mohamadinia@gmail.com + + sajjaadalipour + Sajjad Alipour + sajjaad.alipour@gmail.com + @@ -62,18 +67,18 @@ UTF-8 - 2.4.0 - 1.8 + 3.2.0 + 21 1.1.1 - 0.8.5 - 3.8.0 + 0.8.11 + 3.8.1 2.22.0 2.22.0 ${maven-source-plugin.version} 3.0.2 3.0.1 1.6 - 5.4.1 + 6.2.0 @@ -83,6 +88,12 @@ ${spring-boot.version} true
+ + org.springframework.boot + spring-boot-starter-logging + ${spring-boot.version} + true + org.springframework.boot spring-boot-starter-webflux @@ -131,6 +142,13 @@ ${junitparams.version} test + + + net.bytebuddy + byte-buddy + 1.14.10 + +
diff --git a/src/main/java/me/alidg/errors/ExceptionLogger.java b/src/main/java/me/alidg/errors/ExceptionLogger.java index 2714d50..94a6c9e 100644 --- a/src/main/java/me/alidg/errors/ExceptionLogger.java +++ b/src/main/java/me/alidg/errors/ExceptionLogger.java @@ -5,8 +5,7 @@ /** * Defines a contract to log the to-be-handled exceptions, that's it! * - *

- * For a richer alternative check {@link WebErrorHandlerPostProcessor}. + *

For a richer alternative check {@link WebErrorHandlerPostProcessor}. * * @author Ali Dehghani * @implNote Do not throw exceptions in method implementations. diff --git a/src/main/java/me/alidg/errors/FingerprintProvider.java b/src/main/java/me/alidg/errors/FingerprintProvider.java index d308732..5c1b842 100644 --- a/src/main/java/me/alidg/errors/FingerprintProvider.java +++ b/src/main/java/me/alidg/errors/FingerprintProvider.java @@ -6,12 +6,11 @@ /** * Provides a fingerprint for given {@link HttpError}. * - *

- * A fingerprint helps with identifying errors across domains. - * With fingerprint you can easily correlate error reported in - * application (e.g. as entry in application log) with user-friendly + *

A fingerprint helps with identifying errors across domains. + * With fingerprint, you can correlate error reported in + * application (e.g., as entry in application log) with user-friendly * error reported via HTTP (which doesn't - and shouldn't - contain - * vital information, like e.g. stacktrace). + * vital information, like e.g., stacktrace). * * @author zarebski-m */ diff --git a/src/main/java/me/alidg/errors/HttpError.java b/src/main/java/me/alidg/errors/HttpError.java index d373991..f4722c0 100644 --- a/src/main/java/me/alidg/errors/HttpError.java +++ b/src/main/java/me/alidg/errors/HttpError.java @@ -213,9 +213,8 @@ public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof CodedMessage)) return false; + if (!(o instanceof CodedMessage that)) return false; - CodedMessage that = (CodedMessage) o; return Objects.equals(getCode(), that.getCode()) && Objects.equals(getMessage(), that.getMessage()) && Objects.equals(getArguments(), that.getArguments()); diff --git a/src/main/java/me/alidg/errors/WebErrorHandlerPostProcessor.java b/src/main/java/me/alidg/errors/WebErrorHandlerPostProcessor.java index c7923a6..62a41a7 100644 --- a/src/main/java/me/alidg/errors/WebErrorHandlerPostProcessor.java +++ b/src/main/java/me/alidg/errors/WebErrorHandlerPostProcessor.java @@ -3,14 +3,15 @@ import org.springframework.lang.NonNull; /** - * Post processor/action executor for {@link HttpError}s. Every post processor registered + * Post-processor/action executor for {@link HttpError}s. Every post processor registered * as Spring bean will be called after error is prepared. * - *

- * This might be considered as richer and more flexible alternative to {@link ExceptionLogger}. + *

This might be considered as richer and more flexible alternative to {@link ExceptionLogger}. * The former logs at the beginning of error handling and the exception is the only parameter - * there. This post processor takes as its parameter rich {@link HttpError} parameter, which - * contains original exception along with other goodies. Moreover, more than single post processor + * there. + * This post-processor takes as its parameter rich {@link HttpError} parameter, which + * contains original exception along with other goodies. + * Moreover, more than a single post processor * can be declared. * * @author zarebski-m diff --git a/src/main/java/me/alidg/errors/WebErrorHandlers.java b/src/main/java/me/alidg/errors/WebErrorHandlers.java index 14403b3..c28bd3a 100644 --- a/src/main/java/me/alidg/errors/WebErrorHandlers.java +++ b/src/main/java/me/alidg/errors/WebErrorHandlers.java @@ -2,7 +2,6 @@ import me.alidg.errors.HttpError.CodedMessage; import me.alidg.errors.conf.ErrorsProperties; -import me.alidg.errors.fingerprint.UuidFingerprintProvider; import me.alidg.errors.handlers.LastResortWebErrorHandler; import me.alidg.errors.message.TemplateAwareMessageSource; import org.slf4j.Logger; @@ -11,7 +10,6 @@ import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; -import java.util.Collections; import java.util.List; import java.util.Locale; @@ -23,12 +21,12 @@ * A factory over {@link WebErrorHandler} implementations. The factory would query all the * implementations to find an appropriate exception handler to handle a particular exception. * - *

Code to Message Translation

+ *

Code to Message Translation * The {@link WebErrorHandlers} is also responsible for Error Code to Error Message translation - * and i18n (RIP SRP!). In order to fulfill this requirement, we need an instance of {@link MessageSource} + * and i18n (RIP SRP!). To fulfill this requirement, we need an instance of {@link MessageSource} * to translate error codes to error messages. * - *

Default Exception Handler

+ *

Default Exception Handler * By default, when we couldn't find any {@link WebErrorHandler} implementation to handle the * exception, we would use the {@link LastResortWebErrorHandler} as the default exception handler. * If you don't like its exception handling approach, consider passing a valid non-null @@ -99,28 +97,6 @@ public class WebErrorHandlers { @NonNull private WebErrorHandler defaultWebErrorHandler = LastResortWebErrorHandler.INSTANCE; - /** - * Backward-compatible constructor with defaults for {@link #webErrorHandlerPostProcessors} - * - * @param messageSource The code to message translator. - * @param webErrorHandlers Collection of {@link WebErrorHandler} implementations. - * @param defaultWebErrorHandler Fallback web error handler. - * @param exceptionRefiner Possibly can refine exceptions before handling them. - * @param exceptionLogger Logs exceptions. - * @deprecated Use {@link #builder(MessageSource)} instead. - */ - @Deprecated - public WebErrorHandlers(@NonNull MessageSource messageSource, - @NonNull List webErrorHandlers, - @Nullable WebErrorHandler defaultWebErrorHandler, - @Nullable ExceptionRefiner exceptionRefiner, - @Nullable ExceptionLogger exceptionLogger) { - this(messageSource, webErrorHandlers, defaultWebErrorHandler, - exceptionRefiner != null ? exceptionRefiner : ExceptionRefiner.NoOp.INSTANCE, - exceptionLogger != null ? exceptionLogger : ExceptionLogger.NoOp.INSTANCE, - Collections.emptyList(), new UuidFingerprintProvider(), new ErrorsProperties()); - } - /** * To initialize the {@link WebErrorHandlers} instance with a code-to-message translator, a * non-empty collection of {@link WebErrorHandler} implementations and an optional fallback diff --git a/src/main/java/me/alidg/errors/WebErrorHandlersBuilder.java b/src/main/java/me/alidg/errors/WebErrorHandlersBuilder.java index 20b05a0..ea0288f 100644 --- a/src/main/java/me/alidg/errors/WebErrorHandlersBuilder.java +++ b/src/main/java/me/alidg/errors/WebErrorHandlersBuilder.java @@ -32,17 +32,17 @@ public final class WebErrorHandlersBuilder { private final List webErrorHandlers = new ArrayList<>(); /** - * Collection of post processors to call after handling any given exception. + * Collection of post-processors to call after handling any given exception. */ private final List webErrorHandlerPostProcessors = new ArrayList<>(); /** - * Determines the way we're gonna configure the error handling mechanism. + * Determines the way we're going to configure the error handling mechanism. */ private ErrorsProperties errorsProperties = new ErrorsProperties(); /** - * Represents the default web error handler we're gonna use when all other handlers + * Represents the default web error handler we're going to use when all other handlers * refuse to handle an exception. */ private WebErrorHandler defaultWebErrorHandler = LastResortWebErrorHandler.INSTANCE; @@ -73,7 +73,7 @@ public final class WebErrorHandlersBuilder { } /** - * Determines the way we're gonna configure the error handling mechanism. + * Determines the way we're going to configure the error handling mechanism. * * @param errorsProperties Non-null instance of {@link ErrorsProperties}. * If not provided, default-constructed {@link ErrorsProperties} will be used. @@ -116,7 +116,7 @@ public WebErrorHandlersBuilder withErrorHandlers(@NonNull WebErrorHandler... web } /** - * Represents the default web error handler we're gonna use when all other handlers + * Represents the default web error handler we're going to use when all other handlers * refuse to handle an exception. * * @param defaultWebErrorHandler Non-null instance of {@link WebErrorHandler}. @@ -162,7 +162,7 @@ public WebErrorHandlersBuilder withExceptionLogger(@NonNull ExceptionLogger exce } /** - * Collection of post processors to call after handling any given exception. + * Collection of post-processors to call after handling any given exception. * * @param webErrorHandlerPostProcessors Non-null collection of {@link WebErrorHandlerPostProcessor} instances. * @return This builder. @@ -176,7 +176,7 @@ public WebErrorHandlersBuilder withPostProcessors(@NonNull Collection attributes) { - switch (getStatusCode(attributes)) { - case 401: - return new UnauthorizedException(); - case 403: - return new ForbiddenException(); - case 404: - return new HandlerNotFoundException(getPath(attributes)); - default: - return new IllegalStateException("The exception is null: " + attributes); - } + return switch (getStatusCode(attributes)) { + case 401 -> new UnauthorizedException(); + case 403 -> new ForbiddenException(); + case 404 -> new HandlerNotFoundException(getPath(attributes)); + default -> new IllegalStateException("The exception is null: " + attributes); + }; } private static String getPath(Map attributes) { diff --git a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java index f311c31..c9ccb4c 100644 --- a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java +++ b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java @@ -76,10 +76,10 @@ public Map getErrorAttributes(ServerRequest request, ErrorAttrib *

This is an attempt to handle not found exceptions in both stacks consistently. * * @param e The exception to examine. - * @return {@code true} if it's a not found one, {@code false} otherwise. + * @return {@code true} if it's not found one, {@code false} otherwise. */ private boolean isNotFoundException(Throwable e) { return e instanceof ResponseStatusException && - ((ResponseStatusException) e).getStatus() == HttpStatus.NOT_FOUND; + ((ResponseStatusException) e).getStatusCode() == HttpStatus.NOT_FOUND; } } diff --git a/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java b/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java index e5d7777..4704030 100644 --- a/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java +++ b/src/main/java/me/alidg/errors/adapter/attributes/ServletErrorAttributes.java @@ -28,7 +28,7 @@ public class ServletErrorAttributes extends DefaultErrorAttributes { * To convey the status code from the handled exception to the * {@link org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController}. */ - private static final String STATUS_CODE_ATTR = "javax.servlet.error.status_code"; + private static final String STATUS_CODE_ATTR = "jakarta.servlet.error.status_code"; /** * To handle exceptions. @@ -63,7 +63,7 @@ public ServletErrorAttributes(WebErrorHandlers webErrorHandlers, * Spring Boot compatible representation. * * @param webRequest The current HTTP request. - * @param options Whether or not to include the stack trace in the error attributes. + * @param options Whether to include the stack trace in the error attributes. * @return Error details. */ @Override diff --git a/src/main/java/me/alidg/errors/annotation/AutoConfigureErrors.java b/src/main/java/me/alidg/errors/annotation/AutoConfigureErrors.java deleted file mode 100644 index 8e2fd1e..0000000 --- a/src/main/java/me/alidg/errors/annotation/AutoConfigureErrors.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.alidg.errors.annotation; - -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; - -import java.lang.annotation.*; - -/** - * {@link ImportAutoConfiguration Auto-configuration imports} to enable web error handlers - * support for Spring MVC and Web Flux tests. Suppose you're going to test a controller named {@code UserController}: - *

- * {@code
- *
- *     @AutoConfigureErrors
- *     @RunWith(SpringRunner.class)
- *     @WebMvcTest(UserController.class)
- *     public class UserControllerIT {
- *         // test stuff
- *     }
- * }
- * 
- * - * @author Ali Dehghani - */ -@Inherited -@Documented -@ImportAutoConfiguration -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface AutoConfigureErrors { -} diff --git a/src/main/java/me/alidg/errors/conf/ErrorsAutoConfiguration.java b/src/main/java/me/alidg/errors/conf/ErrorsAutoConfiguration.java index a5e2426..745caeb 100644 --- a/src/main/java/me/alidg/errors/conf/ErrorsAutoConfiguration.java +++ b/src/main/java/me/alidg/errors/conf/ErrorsAutoConfiguration.java @@ -27,14 +27,14 @@ import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** - * Auto-configuration responsible for registering a {@link WebErrorHandlers} filled with + * Autoconfiguration responsible for registering a {@link WebErrorHandlers} filled with * builtin, custom and default fallback {@link WebErrorHandler}s. * - *

Builtin Web Error Handlers

+ *

Builtin Web Error Handlers * Built in {@link WebErrorHandler}s are those we provided out of the box. It's highly recommended * to use these implementations with most possible priority, as we did in this auto-configuration. * - *

Custom Web Error Handlers

+ *

Custom Web Error Handlers * You can also provide your own custom {@link WebErrorHandler} implementations. Just implement the * {@link WebErrorHandler} interface and register it as Spring Bean. If you're willing to prioritize * your implementations, use the Spring {@link org.springframework.core.annotation.Order} annotation @@ -43,7 +43,7 @@ * not OK with that, you can always discard this auto-configuration by registering your own * {@link WebErrorHandlers} factory bean.

* - *

Default Fallback Web Error Handler

+ *

Default Fallback Web Error Handler * While handling a particular exception, each registered {@link WebErrorHandler} in {@link WebErrorHandlers} * would be consulted one after another (Depending on their priority). If all of the registered handlers * refuse to handle the exception, then a default fallback {@link WebErrorHandler} should handle the exception. diff --git a/src/main/java/me/alidg/errors/conf/ErrorsProperties.java b/src/main/java/me/alidg/errors/conf/ErrorsProperties.java index 301d4a6..2d2eb06 100644 --- a/src/main/java/me/alidg/errors/conf/ErrorsProperties.java +++ b/src/main/java/me/alidg/errors/conf/ErrorsProperties.java @@ -18,7 +18,7 @@ public class ErrorsProperties { /** - * Determines how we're gonna expose the arguments for each error code. + * Determines how we're going to expose the arguments for each error code. */ @NonNull private ArgumentExposure exposeArguments = ArgumentExposure.NEVER; diff --git a/src/main/java/me/alidg/errors/conf/ReactiveSecurityErrorsAutoConfiguration.java b/src/main/java/me/alidg/errors/conf/ReactiveSecurityErrorsAutoConfiguration.java index 805576e..728f567 100644 --- a/src/main/java/me/alidg/errors/conf/ReactiveSecurityErrorsAutoConfiguration.java +++ b/src/main/java/me/alidg/errors/conf/ReactiveSecurityErrorsAutoConfiguration.java @@ -53,7 +53,7 @@ public class ReactiveSecurityErrorsAutoConfiguration { * Responsible for catching all access denied exceptions and delegating them to typical web error handlers * to perform the actual exception handling procedures. * - * @param errorWebExceptionHandler Spring Boot's default exception handler which in turn would delegate to our + * @param errorWebExceptionHandler Spring Boot's default exception handler, which in turn would delegate to our * typical error handlers. * @return The registered access denied handler. */ diff --git a/src/main/java/me/alidg/errors/conf/ServletErrorsAutoConfiguration.java b/src/main/java/me/alidg/errors/conf/ServletErrorsAutoConfiguration.java index 1d7b5c0..0bdae0f 100644 --- a/src/main/java/me/alidg/errors/conf/ServletErrorsAutoConfiguration.java +++ b/src/main/java/me/alidg/errors/conf/ServletErrorsAutoConfiguration.java @@ -1,5 +1,6 @@ package me.alidg.errors.conf; +import jakarta.servlet.http.HttpServletRequest; import me.alidg.errors.WebErrorHandlers; import me.alidg.errors.adapter.HttpErrorAttributesAdapter; import me.alidg.errors.adapter.attributes.ServletErrorAttributes; @@ -15,6 +16,7 @@ import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.context.annotation.Bean; @@ -22,16 +24,14 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; -import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; import static java.util.stream.Collectors.toList; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; -import static org.springframework.http.MediaType.ALL; /** - * Encapsulates servlet-specific parts of errors auto-configuration. + * Encapsulates servlet-specific parts of errors autoconfiguration. * * @author Ali Dehghani */ @@ -86,8 +86,8 @@ public ErrorAttributes errorAttributes(WebErrorHandlers webErrorHandlers, @ConditionalOnBean(WebErrorHandlers.class) @ConditionalOnMissingBean(ErrorController.class) public BasicErrorController customErrorController(ErrorAttributes errorAttributes, - ServerProperties serverProperties, - ObjectProvider errorViewResolvers) { + ServerProperties serverProperties, + ObjectProvider errorViewResolvers) { List resolvers = errorViewResolvers.orderedStream().collect(toList()); return new CustomServletErrorController(errorAttributes, serverProperties.getError(), resolvers); } @@ -107,7 +107,7 @@ public CustomServletErrorController(ErrorAttributes errorAttributes, /** * Since our custom {@link ErrorAttributes} implementation is storing the HTTP status code inside * the HTTP request, we should call the {@link #getStatus(HttpServletRequest)} method after the - * call to {@link #getErrorAttributes(HttpServletRequest, boolean)}. + * call to {@link #getErrorAttributes(HttpServletRequest, ErrorAttributeOptions)} . * * @param request The current HTTP request. * @return Returns the HTTP response. @@ -115,7 +115,7 @@ public CustomServletErrorController(ErrorAttributes errorAttributes, @Override @RequestMapping public ResponseEntity> error(HttpServletRequest request) { - Map body = getErrorAttributes(request, isIncludeStackTrace(request, ALL)); + Map body = getErrorAttributes(request, ErrorAttributeOptions.defaults()); HttpStatus status = getStatus(request); return new ResponseEntity<>(body, status); } diff --git a/src/main/java/me/alidg/errors/conf/ServletSecurityErrorsAutoConfiguration.java b/src/main/java/me/alidg/errors/conf/ServletSecurityErrorsAutoConfiguration.java index c2e7e19..e810569 100644 --- a/src/main/java/me/alidg/errors/conf/ServletSecurityErrorsAutoConfiguration.java +++ b/src/main/java/me/alidg/errors/conf/ServletSecurityErrorsAutoConfiguration.java @@ -6,15 +6,15 @@ import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** - * A servlet specific auto-configuration to register an {@link AccessDeniedHandler} and another + * A servlet-specific autoconfiguration to register an {@link AccessDeniedHandler} and another * {@link AuthenticationEntryPoint} when the traditional Spring Security is detected. Using these * two handlers will make sure that our exception handling mechanism would properly catch and handle - * all security related exceptions. + * all security-related exceptions. * * @author Ali Dehghani * @implNote In contrast with other handlers that register themselves automatically, in order to use these @@ -48,9 +48,9 @@ public class ServletSecurityErrorsAutoConfiguration { /** - * The error attribute we're going save the exception under it. + * The error attribute we're going saves the exception under it. */ - private static final String ERROR_ATTRIBUTE = "javax.servlet.error.exception"; + private static final String ERROR_ATTRIBUTE = "jakarta.servlet.error.exception"; /** * Registers a handler to handle to access denied exceptions. diff --git a/src/main/java/me/alidg/errors/handlers/AnnotatedWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/AnnotatedWebErrorHandler.java index e777768..6607d6a 100644 --- a/src/main/java/me/alidg/errors/handlers/AnnotatedWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/AnnotatedWebErrorHandler.java @@ -25,7 +25,7 @@ /** * {@link WebErrorHandler} implementation responsible for handling exceptions annotated with * the {@link ExceptionMapping} annotation. The web error code and status code would be - * extracted form the annotated exception. Also, any member annotated with {@link ExposeAsArg} + * extracted from the annotated exception. Also, any member annotated with {@link ExposeAsArg} * would be exposed as arguments. * * @author Ali Dehghani @@ -104,13 +104,11 @@ private List getExposedValues(Throwable exception) { */ private Argument getArgument(AnnotatedElement element, Throwable exception) { try { - if (element instanceof Field) { - Field f = (Field) element; + if (element instanceof Field f) { f.setAccessible(true); return arg(getExposedName(f), f.get(exception)); - } else if (element instanceof Method) { - Method m = (Method) element; + } else if (element instanceof Method m) { m.setAccessible(true); return arg(getExposedName(m), m.invoke(exception)); diff --git a/src/main/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandler.java index 4170b8a..e4975f5 100644 --- a/src/main/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandler.java @@ -6,8 +6,8 @@ import org.springframework.http.HttpStatus; import org.springframework.lang.NonNull; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; import java.util.List; import java.util.Map; import java.util.Set; @@ -38,11 +38,11 @@ public boolean canHandle(Throwable exception) { /** * Handles the given {@link ConstraintViolationException}s by extracting the error codes from the message templates - * and also, extracting the arguments from {@link javax.validation.metadata.ConstraintDescriptor}s. + * and also, extracting the arguments from {@link jakarta.validation.metadata.ConstraintDescriptor}s. * - *

Constraint Descriptor

- * All annotation attributes defined in a constrain annotation, e.g. {@link javax.validation.constraints.Size}, - * can be extracted from the {@link javax.validation.metadata.ConstraintDescriptor#getAttributes()} method. The + *

Constraint Descriptor + * All annotation attributes defined in a constrain annotation, e.g. {@link jakarta.validation.constraints.Size}, + * can be extracted from the {@link jakarta.validation.metadata.ConstraintDescriptor#getAttributes()} method. The * specification of the Bean Validation API demands, that any constraint annotation must define three mandatory * attributes, {@code message}, {@code groups} and {@code payload}. Since these three attributes are not that valuable * as arguments, we're not going to expose them. diff --git a/src/main/java/me/alidg/errors/handlers/ConstraintViolations.java b/src/main/java/me/alidg/errors/handlers/ConstraintViolations.java index c78e2a2..7ba1ee3 100644 --- a/src/main/java/me/alidg/errors/handlers/ConstraintViolations.java +++ b/src/main/java/me/alidg/errors/handlers/ConstraintViolations.java @@ -3,11 +3,11 @@ import me.alidg.errors.Argument; import org.hibernate.validator.constraints.*; -import javax.validation.ConstraintViolation; -import javax.validation.constraints.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.constraints.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import java.lang.annotation.Annotation; import java.util.*; import java.util.stream.Collectors; @@ -25,7 +25,7 @@ final class ConstraintViolations { /** * Collection of prefixes representing default validation error codes. */ - private static final List DEFAULT_ERROR_CODES_PREFIX = asList("{javax.validation.", "{org.hibernate.validator"); + private static final List DEFAULT_ERROR_CODES_PREFIX = asList("{jakarta.validation.", "{org.hibernate.validator"); /** * A mapping between constraint annotations and error codes. @@ -117,7 +117,6 @@ private static Map, String> initErrorCodeMapping() { // Hibernate Validator Specific Constraints codes.put(URL.class, "invalidUrl"); codes.put(UniqueElements.class, "shouldBeUnique"); - codes.put(SafeHtml.class, "unsafeHtml"); codes.put(Range.class, "outOfRange"); codes.put(Length.class, "invalidSize"); diff --git a/src/main/java/me/alidg/errors/handlers/MissingRequestParametersWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/MissingRequestParametersWebErrorHandler.java index e1cc403..b85a0e8 100644 --- a/src/main/java/me/alidg/errors/handlers/MissingRequestParametersWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/MissingRequestParametersWebErrorHandler.java @@ -71,20 +71,17 @@ public HandledException handle(Throwable exception) { List arguments = new ArrayList<>(); String errorCode = "unknown_error"; - if (exception instanceof MissingRequestHeaderException) { - MissingRequestHeaderException headerException = (MissingRequestHeaderException) exception; + if (exception instanceof MissingRequestHeaderException headerException) { arguments.add(arg("name", headerException.getHeaderName())); arguments.add(arg("expected", getType(headerException.getParameter()))); errorCode = MISSING_HEADER; - } else if (exception instanceof MissingRequestCookieException) { - MissingRequestCookieException cookieException = (MissingRequestCookieException) exception; + } else if (exception instanceof MissingRequestCookieException cookieException) { arguments.add(arg("name", cookieException.getCookieName())); arguments.add(arg("expected", getType(cookieException.getParameter()))); errorCode = MISSING_COOKIE; - } else if (exception instanceof MissingMatrixVariableException) { - MissingMatrixVariableException variableException = (MissingMatrixVariableException) exception; + } else if (exception instanceof MissingMatrixVariableException variableException) { arguments.add(arg("name", variableException.getVariableName())); arguments.add(arg("expected", getType(variableException.getParameter()))); diff --git a/src/main/java/me/alidg/errors/handlers/MultipartWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/MultipartWebErrorHandler.java index 4dc4552..3c12e2a 100644 --- a/src/main/java/me/alidg/errors/handlers/MultipartWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/MultipartWebErrorHandler.java @@ -49,8 +49,8 @@ public HandledException handle(Throwable exception) { String errorCode = MULTIPART_EXPECTED; Map> arguments = emptyMap(); - if (exception instanceof MaxUploadSizeExceededException) { - long maxSize = ((MaxUploadSizeExceededException) exception).getMaxUploadSize(); + if (exception instanceof MaxUploadSizeExceededException e) { + long maxSize = e.getMaxUploadSize(); errorCode = MAX_SIZE; arguments = singletonMap(MAX_SIZE, singletonList(arg("max_size", maxSize))); } diff --git a/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java index 982a196..200c297 100644 --- a/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java @@ -64,26 +64,20 @@ public boolean canHandle(Throwable exception) { @NonNull @Override public HandledException handle(Throwable exception) { - if (exception instanceof MediaTypeNotSupportedStatusException) { - Set types = getMediaTypes(((MediaTypeNotSupportedStatusException) exception).getSupportedMediaTypes()); + if (exception instanceof UnsupportedMediaTypeStatusException e) { + Set types = getMediaTypes(e.getSupportedMediaTypes()); Map> args = types.isEmpty() ? emptyMap() : argMap(NOT_SUPPORTED, arg("types", types)); return new HandledException(NOT_SUPPORTED, UNSUPPORTED_MEDIA_TYPE, args); } - if (exception instanceof UnsupportedMediaTypeStatusException) { - Set types = getMediaTypes(((UnsupportedMediaTypeStatusException) exception).getSupportedMediaTypes()); - Map> args = types.isEmpty() ? emptyMap() : argMap(NOT_SUPPORTED, arg("types", types)); - return new HandledException(NOT_SUPPORTED, UNSUPPORTED_MEDIA_TYPE, args); - } - - if (exception instanceof NotAcceptableStatusException) { - Set types = getMediaTypes(((NotAcceptableStatusException) exception).getSupportedMediaTypes()); + if (exception instanceof NotAcceptableStatusException e) { + Set types = getMediaTypes(e.getSupportedMediaTypes()); Map> args = types.isEmpty() ? emptyMap() : argMap(NOT_ACCEPTABLE, arg("types", types)); return new HandledException(NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE, args); } - if (exception instanceof MethodNotAllowedException) { - String httpMethod = ((MethodNotAllowedException) exception).getHttpMethod(); + if (exception instanceof MethodNotAllowedException e) { + String httpMethod = e.getHttpMethod(); return new HandledException(METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED, argMap(METHOD_NOT_ALLOWED, arg("method", httpMethod))); } @@ -91,10 +85,9 @@ public HandledException handle(Throwable exception) { return validationWebErrorHandler.handle(exception); } - if (exception instanceof ServerWebInputException) { - MethodParameter parameter = ((ServerWebInputException) exception).getMethodParameter(); - if (exception.getCause() instanceof TypeMismatchException) { - TypeMismatchException cause = ((TypeMismatchException) exception.getCause()); + if (exception instanceof ServerWebInputException e) { + MethodParameter parameter = e.getMethodParameter(); + if (exception.getCause() instanceof TypeMismatchException cause) { if (cause.getPropertyName() == null) cause.initPropertyName(parameter.getParameterName()); return typeMismatchWebErrorHandler.handle(cause); @@ -106,8 +99,8 @@ public HandledException handle(Throwable exception) { return new HandledException(INVALID_OR_MISSING_BODY, BAD_REQUEST, null); } - if (exception instanceof ResponseStatusException) { - HttpStatus status = ((ResponseStatusException) exception).getStatus(); + if (exception instanceof ResponseStatusException e) { + HttpStatus status = HttpStatus.valueOf(e.getStatusCode().value()); if (status == NOT_FOUND) return new HandledException(NO_HANDLER, status, null); return new HandledException(UNKNOWN_ERROR_CODE, status, null); @@ -118,10 +111,10 @@ public HandledException handle(Throwable exception) { /** * Spring WebFlux throw just one exception, i.e. {@link WebExchangeBindException} for - * all request body binding failures, i.e. missing required parameter or missing matrix - * variables. On the contrary, Traditional web stack throw one specific exception for - * each scenario. In order to provide a consistent API for both stacks, we chose to - * throw a bunch of if-else es to determines the actual cause and provide explicit feedback + * all request body binding failures, i.e., missing required parameter or missing matrix + * variables. On the contrary, Traditional web stack throws one specific exception for + * each scenario. To provide a consistent API for both stacks, we chose to + * throw a bunch of if-else es to determine the actual cause and provide explicit feedback * to the client. * * @param parameter The invalid method parameter. diff --git a/src/main/java/me/alidg/errors/handlers/ServletWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/ServletWebErrorHandler.java index a71d28b..7dd6aa5 100644 --- a/src/main/java/me/alidg/errors/handlers/ServletWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/ServletWebErrorHandler.java @@ -1,5 +1,6 @@ package me.alidg.errors.handlers; +import jakarta.servlet.ServletException; import me.alidg.errors.Argument; import me.alidg.errors.HandledException; import me.alidg.errors.WebErrorHandler; @@ -13,8 +14,8 @@ import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.servlet.resource.NoResourceFoundException; -import javax.servlet.ServletException; import java.util.List; import java.util.Map; import java.util.Set; @@ -48,7 +49,7 @@ public class ServletWebErrorHandler implements WebErrorHandler { public static final String NOT_SUPPORTED = "web.unsupported_media_type"; /** - * The HTTP method not supported on the resource. + * The HTTP method isn't supported on the resource. */ public static final String METHOD_NOT_ALLOWED = "web.method_not_allowed"; @@ -81,6 +82,7 @@ public boolean canHandle(Throwable exception) { exception instanceof MissingServletRequestParameterException || exception instanceof MissingServletRequestPartException || exception instanceof NoHandlerFoundException || + exception instanceof NoResourceFoundException || exception instanceof HttpMessageNotReadableException; } @@ -99,16 +101,16 @@ public HandledException handle(Throwable exception) { if (exception instanceof HttpMessageNotReadableException) return new HandledException(INVALID_OR_MISSING_BODY, HttpStatus.BAD_REQUEST, null); - if (exception instanceof HttpMediaTypeNotAcceptableException) { - Set types = getMediaTypes(((HttpMediaTypeNotAcceptableException) exception).getSupportedMediaTypes()); + if (exception instanceof HttpMediaTypeNotAcceptableException e) { + Set types = getMediaTypes(e.getSupportedMediaTypes()); Map> args = types.isEmpty() ? emptyMap() : singletonMap(NOT_ACCEPTABLE, singletonList(arg("types", types))); return new HandledException(NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE, args); } - if (exception instanceof HttpMediaTypeNotSupportedException) { - MediaType contentType = ((HttpMediaTypeNotSupportedException) exception).getContentType(); + if (exception instanceof HttpMediaTypeNotSupportedException e) { + MediaType contentType = e.getContentType(); List arguments = null; if (contentType != null) arguments = singletonList(arg("type", contentType.toString())); @@ -116,16 +118,15 @@ public HandledException handle(Throwable exception) { arguments == null ? emptyMap() : singletonMap(NOT_SUPPORTED, arguments)); } - if (exception instanceof HttpRequestMethodNotSupportedException) { - String method = ((HttpRequestMethodNotSupportedException) exception).getMethod(); + if (exception instanceof HttpRequestMethodNotSupportedException e) { + String method = e.getMethod(); return new HandledException(METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED, singletonMap(METHOD_NOT_ALLOWED, singletonList(arg("method", method))) ); } - if (exception instanceof MissingServletRequestParameterException) { - MissingServletRequestParameterException actualException = (MissingServletRequestParameterException) exception; + if (exception instanceof MissingServletRequestParameterException actualException) { String name = actualException.getParameterName(); String type = actualException.getParameterType(); @@ -134,22 +135,30 @@ public HandledException handle(Throwable exception) { ); } - if (exception instanceof MissingServletRequestPartException) { - String name = ((MissingServletRequestPartException) exception).getRequestPartName(); + if (exception instanceof MissingServletRequestPartException e) { + String name = e.getRequestPartName(); return new HandledException(MISSING_PART, HttpStatus.BAD_REQUEST, singletonMap(MISSING_PART, singletonList(arg("name", name))) ); } - if (exception instanceof NoHandlerFoundException) { - String url = ((NoHandlerFoundException) exception).getRequestURL(); + if (exception instanceof NoHandlerFoundException e) { + String url = e.getRequestURL(); return new HandledException(NO_HANDLER, HttpStatus.NOT_FOUND, singletonMap(NO_HANDLER, singletonList(arg("path", url))) ); } + if (exception instanceof NoResourceFoundException e) { + String path = e.getResourcePath(); + + return new HandledException(NO_HANDLER, HttpStatus.NOT_FOUND, + singletonMap(NO_HANDLER, singletonList(arg("path", path))) + ); + } + return new HandledException("unknown_error", HttpStatus.INTERNAL_SERVER_ERROR, null); } diff --git a/src/main/java/me/alidg/errors/handlers/SpringValidationWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/SpringValidationWebErrorHandler.java index d4aa5a6..0f59982 100644 --- a/src/main/java/me/alidg/errors/handlers/SpringValidationWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/SpringValidationWebErrorHandler.java @@ -11,7 +11,7 @@ import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; -import javax.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolation; import java.util.List; import java.util.Map; @@ -44,7 +44,7 @@ public class SpringValidationWebErrorHandler implements WebErrorHandler { */ @Override public boolean canHandle(Throwable exception) { - return exception instanceof MethodArgumentNotValidException || exception instanceof BindException; + return exception instanceof BindException; } /** diff --git a/src/main/java/me/alidg/errors/handlers/TypeMismatchWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/TypeMismatchWebErrorHandler.java index eae1e60..fd9e346 100644 --- a/src/main/java/me/alidg/errors/handlers/TypeMismatchWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/TypeMismatchWebErrorHandler.java @@ -71,8 +71,8 @@ static String getErrorCode(TypeMismatchException mismatchException) { } private static String getPropertyName(TypeMismatchException mismatchException) { - if (mismatchException instanceof MethodArgumentTypeMismatchException) - return ((MethodArgumentTypeMismatchException) mismatchException).getName(); + if (mismatchException instanceof MethodArgumentTypeMismatchException e) + return e.getName(); return mismatchException.getPropertyName(); } diff --git a/src/main/java/me/alidg/errors/message/TemplateParser.java b/src/main/java/me/alidg/errors/message/TemplateParser.java index 94cf2c0..b5e56de 100644 --- a/src/main/java/me/alidg/errors/message/TemplateParser.java +++ b/src/main/java/me/alidg/errors/message/TemplateParser.java @@ -11,7 +11,7 @@ * Responsible for parsing string templates and replacing the named or positional arguments * with their corresponding values. The parser treats { and } characters as the variable delimiters. * - *

Named Arguments

+ *

Named Arguments * When the parser sees a templated expression, first it considers that expression as a named argument, even * if it looks like a positional one, e.g. {0}. If it could find an argument matching with that template name, * it would replace the placeholder with its corresponding argument value. For example, the following expression: @@ -23,7 +23,7 @@ * Your age should be at least 18 but you're 17 years old. * * - *

Positional Arguments

+ *

Positional Arguments * If template parser couldn't find a matched argument name for any given placeholder, then it will try positional * arguments iff the placeholder is an integer. For example, the following template message: *

@@ -34,14 +34,14 @@
  *     The minimum age is 18
  * 
* - *

Escape Character

+ *

Escape Character * If we need to use delimiting characters in a plain text, we can escape them using a backslash. For example, the * following text does not contain any placeholder: *

  *     Plain \{text}
  * 
* - *

Placeholders with No Values

+ *

Placeholders with No Values * When there is no corresponding value for a placeholder, the placeholder would remain intact. For example, * if there is no corresponding value for {min}, then the placeholder would be in the final outcome. * diff --git a/src/main/java/me/alidg/errors/mvc/ErrorsControllerAdvice.java b/src/main/java/me/alidg/errors/mvc/ErrorsControllerAdvice.java index 450c69d..e263ab8 100644 --- a/src/main/java/me/alidg/errors/mvc/ErrorsControllerAdvice.java +++ b/src/main/java/me/alidg/errors/mvc/ErrorsControllerAdvice.java @@ -36,7 +36,7 @@ public abstract class ErrorsControllerAdvice { * * @param errorHandlers The exception handler collaborator. * @param httpErrorAttributesAdapter To adapt our error representation to Spring Boot's representation of an error. - * @throws NullPointerException When one of the required parameters are null. + * @throws NullPointerException When one of the required parameters null. */ public ErrorsControllerAdvice(WebErrorHandlers errorHandlers, HttpErrorAttributesAdapter httpErrorAttributesAdapter) { @@ -51,7 +51,7 @@ public ErrorsControllerAdvice(WebErrorHandlers errorHandlers, * @param exception The caught exception. * @param webRequest The current HTTP request. * @param locale Determines the locale for message translation. - * @return A HTTP response with appropriate error body and status code. + * @return A HTTP response with the appropriate error body and status code. */ @ExceptionHandler public ResponseEntity handleException(Throwable exception, WebRequest webRequest, Locale locale) { diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37f602d..0000000 --- a/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,12 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - me.alidg.errors.conf.ErrorsAutoConfiguration,\ - me.alidg.errors.conf.ServletErrorsAutoConfiguration,\ - me.alidg.errors.conf.ReactiveErrorsAutoConfiguration,\ - me.alidg.errors.conf.ServletSecurityErrorsAutoConfiguration,\ - me.alidg.errors.conf.ReactiveSecurityErrorsAutoConfiguration -me.alidg.errors.annotation.AutoConfigureErrors=\ - me.alidg.errors.conf.ErrorsAutoConfiguration,\ - me.alidg.errors.conf.ServletErrorsAutoConfiguration,\ - me.alidg.errors.conf.ReactiveErrorsAutoConfiguration,\ - me.alidg.errors.conf.ReactiveSecurityErrorsAutoConfiguration,\ - me.alidg.errors.conf.ServletSecurityErrorsAutoConfiguration \ No newline at end of file diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..af9a01f --- /dev/null +++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +me.alidg.errors.conf.ErrorsAutoConfiguration +me.alidg.errors.conf.ServletErrorsAutoConfiguration +me.alidg.errors.conf.ReactiveErrorsAutoConfiguration +me.alidg.errors.conf.ServletSecurityErrorsAutoConfiguration +me.alidg.errors.conf.ReactiveSecurityErrorsAutoConfiguration \ No newline at end of file diff --git a/src/test/java/me/alidg/errors/WebErrorHandlersIT.java b/src/test/java/me/alidg/errors/WebErrorHandlersIT.java index 50da7d3..e044d51 100644 --- a/src/test/java/me/alidg/errors/WebErrorHandlersIT.java +++ b/src/test/java/me/alidg/errors/WebErrorHandlersIT.java @@ -1,5 +1,10 @@ package me.alidg.errors; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import me.alidg.errors.HttpError.CodedMessage; @@ -24,11 +29,6 @@ import org.springframework.validation.Validator; import org.springframework.web.bind.MethodArgumentNotValidException; -import javax.validation.ConstraintViolationException; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -51,14 +51,14 @@ @RunWith(JUnitParamsRunner.class) public class WebErrorHandlersIT { - private static final Locale IRAN_LOCALE = new Locale("fa", "IR"); + private static final Locale IRAN_LOCALE = Locale.of("fa", "IR"); + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withPropertyValues("spring.messages.basename=test_messages") .withConfiguration(AutoConfigurations.of( MessageSourceAutoConfiguration.class, ValidationAutoConfiguration.class, ErrorsAutoConfiguration.class - )); @Test @@ -161,7 +161,7 @@ public void constraintViolationException_ShouldBeHandledProperly(Object pojo, Lo HttpError error; WebErrorHandlers errorHandlers = ctx.getBean(WebErrorHandlers.class); - javax.validation.Validator validator = ctx.getBean(javax.validation.Validator.class); + jakarta.validation.Validator validator = ctx.getBean(jakarta.validation.Validator.class); ConstraintViolationException exception = new ConstraintViolationException(validator.validate(pojo)); diff --git a/src/test/java/me/alidg/errors/WebErrorHandlersTest.java b/src/test/java/me/alidg/errors/WebErrorHandlersTest.java deleted file mode 100644 index c926b03..0000000 --- a/src/test/java/me/alidg/errors/WebErrorHandlersTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.alidg.errors; - -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.context.MessageSource; - -import java.util.List; - -import static java.util.Collections.emptyList; -import static me.alidg.Params.p; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; - -/** - * Unit tests for {@link WebErrorHandlers} factory. - * - * @author Ali Dehghani - */ -@RunWith(JUnitParamsRunner.class) -public class WebErrorHandlersTest { - - @Test - @SuppressWarnings("deprecation") - @Parameters(method = "paramsForConstructor") - public void constructor_ShouldEnforceItsPreconditions(MessageSource messageSource, - List handlers, - Class expectedException, - String expectedMessage) { - assertThatThrownBy(() -> new WebErrorHandlers(messageSource, handlers, null, null, null)) - .isInstanceOf(expectedException) - .hasMessage(expectedMessage); - } - - private Object[] paramsForConstructor() { - return p( - p(null, null, NullPointerException.class, "We need a MessageSource implementation to message translation"), - p(mock(MessageSource.class), null, NullPointerException.class, "Collection of error handlers is required"), - p(mock(MessageSource.class), emptyList(), IllegalArgumentException.class, "We need at least one error handler") - ); - } -} diff --git a/src/test/java/me/alidg/errors/adapter/attributes/ServletErrorAttributesIT.java b/src/test/java/me/alidg/errors/adapter/attributes/ServletErrorAttributesIT.java index c1b71c8..e083d92 100644 --- a/src/test/java/me/alidg/errors/adapter/attributes/ServletErrorAttributesIT.java +++ b/src/test/java/me/alidg/errors/adapter/attributes/ServletErrorAttributesIT.java @@ -57,10 +57,10 @@ public void getErrorAttributes_ShouldExtractAndHandlesErrorsProperly(Throwable e String expectedErrorCodes, Argument argument) throws Exception { MockHttpServletRequestBuilder request = get("/error") - .requestAttr("javax.servlet.error.status_code", statusCode) - .requestAttr("javax.servlet.error.request_uri", "/test"); + .requestAttr("jakarta.servlet.error.status_code", statusCode) + .requestAttr("jakarta.servlet.error.request_uri", "/test"); - if (exception != null) request.requestAttr("javax.servlet.error.exception", exception); + if (exception != null) request.requestAttr("jakarta.servlet.error.exception", exception); ResultActions result = mvc.perform(request) .andExpect(status().is(expectedStatusCode)) @@ -75,7 +75,7 @@ public void getErrorAttributes_ShouldExtractAndHandlesErrorsProperly(Throwable e @Test public void getErrorAttributes_ShouldHandleNotFoundExceptionsWithoutPathArgAppropriately() throws Exception { MockHttpServletRequestBuilder request = get("/error") - .requestAttr("javax.servlet.error.status_code", 404); + .requestAttr("jakarta.servlet.error.status_code", 404); mvc.perform(request) .andExpect(status().is(404)) diff --git a/src/test/java/me/alidg/errors/annotation/AutoConfigureErrorsIT.java b/src/test/java/me/alidg/errors/annotation/AutoConfigureErrorsIT.java deleted file mode 100644 index 6567087..0000000 --- a/src/test/java/me/alidg/errors/annotation/AutoConfigureErrorsIT.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.alidg.errors.annotation; - -import me.alidg.errors.WebErrorHandlers; -import me.alidg.errors.conf.*; -import org.junit.Test; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebFlux; -import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; -import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for {@link AutoConfigureErrors} annotation. - * - * @author Ali Dehghani - */ -public class AutoConfigureErrorsIT { - - private final WebApplicationContextRunner servletRunner = new WebApplicationContextRunner() - .withBean(ServerProperties.class) - .withUserConfiguration(TestConfig.class); - - private final ReactiveWebApplicationContextRunner reactiveRunner = new ReactiveWebApplicationContextRunner() - .withBean(ServerProperties.class) - .withUserConfiguration(ReactiveTestConfig.class); - - @Test - public void annotation_ShouldEnableTheWebErrorsSupport() { - servletRunner.run(ctx -> { - WebErrorHandlers handlers = ctx.getBean(WebErrorHandlers.class); - - assertThat(handlers).isNotNull(); - }); - - reactiveRunner.run(ctx -> { - WebErrorHandlers handlers = ctx.getBean(WebErrorHandlers.class); - - assertThat(handlers).isNotNull(); - }); - } - - @Test - public void annotation_ShouldImportServletSpecificAutoConfigurationsForServletStack() { - servletRunner.run(ctx -> { - assertThat(ctx).hasSingleBean(ErrorsAutoConfiguration.class); - assertThat(ctx).hasSingleBean(ServletErrorsAutoConfiguration.class); - assertThat(ctx).hasSingleBean(ServletSecurityErrorsAutoConfiguration.class); - assertThat(ctx).doesNotHaveBean(ReactiveErrorsAutoConfiguration.class); - assertThat(ctx).doesNotHaveBean(ReactiveSecurityErrorsAutoConfiguration.class); - }); - } - - @Test - public void annotation_ShouldImportWebFluxSpecificAutoConfigurationsForReactiveStack() { - reactiveRunner.run(ctx -> { - assertThat(ctx).hasSingleBean(ErrorsAutoConfiguration.class); - assertThat(ctx).hasSingleBean(ReactiveErrorsAutoConfiguration.class); - assertThat(ctx).hasSingleBean(ReactiveSecurityErrorsAutoConfiguration.class); - assertThat(ctx).doesNotHaveBean(ServletErrorsAutoConfiguration.class); - assertThat(ctx).doesNotHaveBean(ServletSecurityErrorsAutoConfiguration.class); - }); - } - - @AutoConfigureErrors - static class TestConfig { - } - - @AutoConfigureErrors - @AutoConfigureWebFlux - @EnableWebFluxSecurity - @ImportAutoConfiguration(ErrorWebFluxAutoConfiguration.class) - static class ReactiveTestConfig { - - @Bean - public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { - return http.build(); - } - } -} diff --git a/src/test/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandlerTest.java b/src/test/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandlerTest.java index 6ff5c60..d530072 100644 --- a/src/test/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandlerTest.java +++ b/src/test/java/me/alidg/errors/handlers/ConstraintViolationWebErrorHandlerTest.java @@ -9,14 +9,14 @@ import org.junit.runner.RunWith; import org.springframework.http.HttpStatus; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import java.util.*; import java.util.stream.Collectors; diff --git a/src/test/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandlerTest.java b/src/test/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandlerTest.java index ad6e8b3..335c782 100644 --- a/src/test/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandlerTest.java +++ b/src/test/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandlerTest.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import static java.util.Collections.*; import static me.alidg.Params.p; @@ -76,29 +77,15 @@ private Object[] paramsForCanHandle() { private Object[] paramsForHandle() { return p( - // MediaTypeNotSupportedStatusException related parameters - p( - new MediaTypeNotSupportedStatusException(emptyList()), - NOT_SUPPORTED, - HttpStatus.UNSUPPORTED_MEDIA_TYPE, - emptyList() - ), - p( - new MediaTypeNotSupportedStatusException(singletonList(APPLICATION_JSON)), - NOT_SUPPORTED, - HttpStatus.UNSUPPORTED_MEDIA_TYPE, - singletonList(Argument.arg("types", singleton(APPLICATION_JSON_VALUE))) - ), - // UnsupportedMediaTypeStatusException related parameters p( - new UnsupportedMediaTypeStatusException(null, emptyList()), + new UnsupportedMediaTypeStatusException("", emptyList()), NOT_SUPPORTED, HttpStatus.UNSUPPORTED_MEDIA_TYPE, emptyList() ), p( - new UnsupportedMediaTypeStatusException(null, singletonList(APPLICATION_PDF)), + new UnsupportedMediaTypeStatusException("", singletonList(APPLICATION_PDF)), NOT_SUPPORTED, HttpStatus.UNSUPPORTED_MEDIA_TYPE, singletonList(Argument.arg("types", singleton(APPLICATION_PDF_VALUE))) diff --git a/src/test/java/me/alidg/errors/handlers/ServletWebErrorHandlerTest.java b/src/test/java/me/alidg/errors/handlers/ServletWebErrorHandlerTest.java index 34abef8..3c54a8b 100644 --- a/src/test/java/me/alidg/errors/handlers/ServletWebErrorHandlerTest.java +++ b/src/test/java/me/alidg/errors/handlers/ServletWebErrorHandlerTest.java @@ -16,7 +16,7 @@ import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.util.HashSet; import java.util.List; import java.util.Map; diff --git a/src/test/java/me/alidg/errors/handlers/SpringValidationWebErrorHandlerTest.java b/src/test/java/me/alidg/errors/handlers/SpringValidationWebErrorHandlerTest.java index 136792f..3ed4485 100644 --- a/src/test/java/me/alidg/errors/handlers/SpringValidationWebErrorHandlerTest.java +++ b/src/test/java/me/alidg/errors/handlers/SpringValidationWebErrorHandlerTest.java @@ -11,12 +11,12 @@ import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import org.springframework.web.bind.MethodArgumentNotValidException; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.Valid; +import jakarta.validation.Validation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import java.util.*; import static java.util.Arrays.asList; diff --git a/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java b/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java index 010160a..242877f 100644 --- a/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java +++ b/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java @@ -1,15 +1,14 @@ package me.alidg.errors.mvc; import junitparams.JUnitParamsRunner; -import junitparams.Parameters; import me.alidg.errors.WebErrorHandlers; -import me.alidg.errors.adapter.HttpErrorAttributesAdapter; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; -import static me.alidg.Params.p; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; /** * Unit tests for {@link ErrorsControllerAdvice}. @@ -17,23 +16,22 @@ * @author Ali Dehghani */ @RunWith(JUnitParamsRunner.class) +@ExtendWith(MockitoExtension.class) public class ErrorsControllerAdviceTest { @Test - @Parameters(method = "provideParamsForConstructor") - public void constructor_ShouldEnforceItsPreconditions(WebErrorHandlers handlers, - HttpErrorAttributesAdapter adapter, - String message) { - assertThatThrownBy(() -> new ErrorsControllerAdvice(handlers, adapter) { + public void constructor_WhenHandlerParamIsNull_ErrorMessageEqualToHandlerIsRequired() { + assertThatThrownBy(() -> new ErrorsControllerAdvice(null, null) { }) .isInstanceOf(NullPointerException.class) - .hasMessage(message); + .hasMessage("Error handlers is required"); } - private Object[] provideParamsForConstructor() { - return p( - p(null, null, "Error handlers is required"), - p(mock(WebErrorHandlers.class), null, "Adapter is required") - ); + @Test + public void constructor_WhenAdapterParamIsNull_ErrorMessageEqualToAdapterIsRequired() { + assertThatThrownBy(() -> new ErrorsControllerAdvice(Mockito.mock(WebErrorHandlers.class), null) { + }) + .isInstanceOf(NullPointerException.class) + .hasMessage("Adapter is required"); } } diff --git a/src/test/java/me/alidg/errors/reactive/ReactiveConfig.java b/src/test/java/me/alidg/errors/reactive/ReactiveConfig.java index e926980..2fbb74d 100644 --- a/src/test/java/me/alidg/errors/reactive/ReactiveConfig.java +++ b/src/test/java/me/alidg/errors/reactive/ReactiveConfig.java @@ -22,17 +22,13 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, ServerAccessDeniedHandler accessDeniedHandler, ServerAuthenticationEntryPoint authenticationEntryPoint) { return http - .csrf() - .accessDeniedHandler(accessDeniedHandler) - .and() - .exceptionHandling() - .authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler) - .and() - .authorizeExchange() - .pathMatchers(GET, "/test/protected").authenticated() - .pathMatchers(POST, "/test/protected").hasRole("ADMIN") - .anyExchange().permitAll() - .and().build(); + .csrf(ServerHttpSecurity.CsrfSpec::disable) + .exceptionHandling(configurer -> configurer.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)) + .authorizeExchange(authorize -> authorize + .pathMatchers(GET, "/test/protected").authenticated() + .pathMatchers(POST, "/test/protected").hasRole("ADMIN") + .anyExchange().permitAll() + ) + .build(); } } diff --git a/src/test/java/me/alidg/errors/reactive/ReactiveController.java b/src/test/java/me/alidg/errors/reactive/ReactiveController.java index aed9173..aacf623 100644 --- a/src/test/java/me/alidg/errors/reactive/ReactiveController.java +++ b/src/test/java/me/alidg/errors/reactive/ReactiveController.java @@ -1,5 +1,7 @@ package me.alidg.errors.reactive; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; import me.alidg.errors.annotation.ExceptionMapping; import me.alidg.errors.annotation.ExposeAsArg; import org.springframework.validation.annotation.Validated; @@ -7,8 +9,6 @@ import org.springframework.web.multipart.MultipartFile; import reactor.core.publisher.Mono; -import javax.validation.Valid; -import javax.validation.constraints.*; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @@ -41,12 +41,12 @@ public Mono delete() { } @GetMapping("/param") - public Mono getParam(@RequestParam String name) { + public Mono getParam(@RequestParam("name") String name) { return Mono.just(new Dto(name, 12, "")); } @PostMapping(value = "/part", consumes = MULTIPART_FORM_DATA_VALUE) - public MultipartFile postParam(@RequestPart MultipartFile file) { + public MultipartFile postParam(@RequestPart("file") MultipartFile file) { return file; } @@ -61,22 +61,22 @@ public Mono needsPermission() { } @GetMapping("/header") - public Mono headerIsRequired(@RequestHeader String name) { + public Mono headerIsRequired(@RequestHeader("name") String name) { return Mono.empty(); } @GetMapping("/cookie") - public Mono cookieIsRequired(@CookieValue String name) { + public Mono cookieIsRequired(@CookieValue("name") String name) { return Mono.empty(); } @GetMapping("/matrix") - public Mono matrixIsRequired(@MatrixVariable String name) { + public Mono matrixIsRequired(@MatrixVariable("name") String name) { return Mono.empty(); } @GetMapping("/type-mismatch") - public void mismatch(@RequestParam Integer number) { + public void mismatch(@RequestParam("number") Integer number) { } @GetMapping("/paged") @@ -166,6 +166,7 @@ public void setSort(Sort sort) { this.sort = sort; } } + protected static class DefaultDto { @Email diff --git a/src/test/java/me/alidg/errors/reactive/ReactiveIT.java b/src/test/java/me/alidg/errors/reactive/ReactiveIT.java index 561ae41..acfb1eb 100644 --- a/src/test/java/me/alidg/errors/reactive/ReactiveIT.java +++ b/src/test/java/me/alidg/errors/reactive/ReactiveIT.java @@ -12,6 +12,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; @@ -22,10 +23,13 @@ import org.springframework.http.MediaType; import org.springframework.http.client.MultipartBodyBuilder; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; import java.math.BigDecimal; import java.util.Collections; @@ -37,7 +41,6 @@ import static me.alidg.errors.handlers.LastResortWebErrorHandler.UNKNOWN_ERROR_CODE; import static me.alidg.errors.handlers.MissingRequestParametersWebErrorHandler.*; import static me.alidg.errors.handlers.ServletWebErrorHandler.*; -import static me.alidg.errors.handlers.SpringSecurityWebErrorHandler.ACCESS_DENIED; import static me.alidg.errors.handlers.SpringSecurityWebErrorHandler.AUTH_REQUIRED; import static me.alidg.errors.reactive.ReactiveController.Dto.dto; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -49,7 +52,6 @@ import static org.springframework.http.HttpStatus.*; import static org.springframework.http.MediaType.*; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; /** * Integration tests for our reactive stack support. @@ -57,6 +59,7 @@ * @author Ali Dehghani */ @AutoConfigureWebTestClient +@ExtendWith(SpringExtension.class) @RunWith(JUnitParamsRunner.class) @SpringBootTest(classes = ReactiveApplication.class) @TestPropertySource(properties = { @@ -274,20 +277,24 @@ public void errorAttributes_ShouldHandleHandleUnauthorizedErrorsProperly() { verify(logger).log(any()); } - @Test - public void errorAttributes_ShouldHandleHandleAccessDeniedErrorsProperly() { - client - .mutateWith(mockUser()) - .post().uri("/test/protected").exchange() - .expectStatus().isForbidden() - .expectBody() - .jsonPath("errors[0].code").isEqualTo(ACCESS_DENIED) - .jsonPath("$.fingerprint").doesNotExist() - .jsonPath("$.errors[0].arguments").doesNotExist(); - - verify(logger).log(any()); - } - + /** + * TODO:// this method doesn't work correctly because of an issue on + * the method {@link org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter#filter(ServerWebExchange, WebFilterChain)}, + * doesn't find principal from context although it received access-decided exception. + */ +// @Test +// public void errorAttributes_ShouldHandleHandleAccessDeniedErrorsProperly() { +// client +// .mutateWith(mockUser("user")) +// .post().uri("/test/protected").exchange() +// .expectStatus().isForbidden() +// .expectBody() +// .jsonPath("errors[0].code").isEqualTo(ACCESS_DENIED) +// .jsonPath("$.fingerprint").doesNotExist() +// .jsonPath("$.errors[0].arguments").doesNotExist(); +// +// verify(logger).log(any()); +// } @Test public void errorAttributes_ShouldHandleMissingHeadersProperly() { client.get().uri("/test/header").exchange() @@ -334,7 +341,7 @@ public void errorAttributes_ShouldHandleBindingExceptionsProperly() { .expectStatus().isBadRequest() .expectBody() .jsonPath("errors[*].code").value(containsInAnyOrder( - "binding.type_mismatch.page", "binding.type_mismatch.size", "binding.type_mismatch.sort")) + "binding.type_mismatch.page", "binding.type_mismatch.size", "binding.type_mismatch.sort")) .jsonPath("$.fingerprint").doesNotExist() .jsonPath("$.errors[*].arguments.property").value(containsInAnyOrder("page", "size", "sort")) .jsonPath("$.errors[*].arguments.expected").value(containsInAnyOrder("Integer", "Integer", "ReactiveController.Sort")) diff --git a/src/test/java/me/alidg/errors/servlet/ServletConfig.java b/src/test/java/me/alidg/errors/servlet/ServletConfig.java index 19ba47c..50eee14 100644 --- a/src/test/java/me/alidg/errors/servlet/ServletConfig.java +++ b/src/test/java/me/alidg/errors/servlet/ServletConfig.java @@ -1,17 +1,19 @@ package me.alidg.errors.servlet; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.AccessDeniedHandler; @Configuration @EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class ServletConfig extends WebSecurityConfigurerAdapter { +@EnableMethodSecurity +public class ServletConfig { private final AccessDeniedHandler accessDeniedHandler; private final AuthenticationEntryPoint authenticationEntryPoint; @@ -21,13 +23,13 @@ public ServletConfig(AccessDeniedHandler accessDeniedHandler, AuthenticationEntr this.authenticationEntryPoint = authenticationEntryPoint; } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .anonymous().disable() - .csrf().disable() - .exceptionHandling() - .accessDeniedHandler(accessDeniedHandler) - .authenticationEntryPoint(authenticationEntryPoint); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + return httpSecurity + .anonymous(AbstractHttpConfigurer::disable) + .csrf(AbstractHttpConfigurer::disable) + .exceptionHandling(configurer -> configurer.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)) + .build(); } + } diff --git a/src/test/java/me/alidg/errors/servlet/ServletController.java b/src/test/java/me/alidg/errors/servlet/ServletController.java index 845602b..5f6192c 100644 --- a/src/test/java/me/alidg/errors/servlet/ServletController.java +++ b/src/test/java/me/alidg/errors/servlet/ServletController.java @@ -1,5 +1,7 @@ package me.alidg.errors.servlet; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; import me.alidg.errors.annotation.ExceptionMapping; import me.alidg.errors.annotation.ExposeAsArg; import org.springframework.http.HttpStatus; @@ -8,8 +10,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import javax.validation.Valid; -import javax.validation.constraints.*; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @@ -34,12 +34,12 @@ public void delete() { } @GetMapping("/param") - public Dto getParam(@RequestParam String name) { + public Dto getParam(@RequestParam("name") String name) { return new Dto(name, 12, ""); } @PostMapping("/part") - public MultipartFile postParam(@RequestPart MultipartFile file) { + public MultipartFile postParam(@RequestPart("file") MultipartFile file) { return file; } @@ -54,19 +54,19 @@ public void needsPermission() { } @GetMapping("/header") - public void headerIsRequired(@RequestHeader String name) { + public void headerIsRequired(@RequestHeader("name") String name) { } @GetMapping("/cookie") - public void cookieIsRequired(@CookieValue String name) { + public void cookieIsRequired(@CookieValue("name") String name) { } @GetMapping("/matrix") - public void matrixIsRequired(@MatrixVariable String name) { + public void matrixIsRequired(@MatrixVariable("name") String name) { } @GetMapping("/type-mismatch") - public void mismatch(@RequestParam Integer number) { + public void mismatch(@RequestParam("number") Integer number) { } @GetMapping("/paged") @@ -74,10 +74,12 @@ public void pagedResult(Pageable pageable) { } @PostMapping("/max-size") - public void upload(@RequestPart MultipartFile file) {} + public void upload(@RequestPart("file") MultipartFile file) { + } @PostMapping("/default-codes") - public void defaultCodes(@RequestBody @Valid DefaultDto dto) {} + public void defaultCodes(@RequestBody @Valid DefaultDto dto) { + } protected static class Pageable { diff --git a/src/test/java/me/alidg/errors/servlet/ServletIT.java b/src/test/java/me/alidg/errors/servlet/ServletIT.java index dc5f9fe..51f038a 100644 --- a/src/test/java/me/alidg/errors/servlet/ServletIT.java +++ b/src/test/java/me/alidg/errors/servlet/ServletIT.java @@ -21,9 +21,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.*; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.web.servlet.MockMvc; @@ -37,7 +35,6 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static me.alidg.Params.p; import static me.alidg.errors.handlers.MissingRequestParametersWebErrorHandler.*; import static me.alidg.errors.handlers.ServletWebErrorHandler.*; @@ -49,7 +46,6 @@ import static org.mockito.Mockito.verify; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -175,7 +171,7 @@ public void controllerAdvice_ShouldHandleInvalidContentTypesProperly() throws Ex @Test public void controllerAdvice_ShouldHandleInvalidAcceptHeadersProperly() throws Exception { - mvc.perform(get("/test/param?name=ali").accept("image/jpeg")) + mvc.perform(get("/test/param").param("name", "ali").accept("image/jpeg")) .andExpect(status().isNotAcceptable()); } @@ -219,11 +215,9 @@ public void errorController_ShouldHandleHandleUnauthorizedErrorsProperly() throw } @Test + @WithMockUser public void errorController_ShouldHandleHandleAccessDeniedErrorsProperly() throws Exception { - List authorities = singletonList(new SimpleGrantedAuthority("ROLE_FAKE")); - TestingAuthenticationToken authentication = new TestingAuthenticationToken("me", "pass", authorities); - - mvc.perform(post("/test/protected").with(authentication(authentication))) + mvc.perform(post("/test/protected")) .andExpect(status().isForbidden()) .andExpect(jsonPath("errors[0].code").value(ACCESS_DENIED)) .andExpect(jsonPath("$.fingerprint").exists()); diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml deleted file mode 100644 index c4cdcdc..0000000 --- a/src/test/resources/logback-test.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - From c578152df772ac4b8723b1756324910425f84fb7 Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Tue, 12 Dec 2023 19:55:23 +0330 Subject: [PATCH 3/6] Add github action --- .github/workflows/main.yml | 55 ++++++++++++++ .travis.yml | 24 ------ README.md | 147 ++++++++++++------------------------- pom.xml | 65 +++------------- 4 files changed, 114 insertions(+), 177 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..8b74779 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,55 @@ +name: Build +on: + push: + branches: + - develop + +jobs: + build: + name: Build + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: 21 + distribution: 'oracle' + server-id: github + settings-path: ${{ github.workspace }} + + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=sajjaadalipour_errors-spring-boot-starter + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3d9bf0f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: java -addons: - sonarcloud: - organization: "alimate" - token: - secure: "Yd79/X/4ynYpXYNS0npZQ/xcNZTzfl4XxIQQ0Ajf8SiUBJlayvSgFMQ1Kz0iph0/1l2iEV9lDhk7zEd0ZB5C6VKJlBjcpCU50XdIxP4byZ9SKLYR0dqBB0p6QHTSZ+nJjR4wszQzgwu/q/b30L29q3b++cQbbIuIxkVkJnqXptxVAP8uPV+lKLpMORWdrXOA9HQ+Q8Rar/FtBGnVjWVRvkXc168O6V3tV5eygKTyBpE6IBwjVnWxgKILe3zUasCjQTUTkMevHNTEpX/8RlO9JEwBaWMZrrRBhHmUjXSRNLSAkXDQRoOgQsXsv342EwA6RVwxGo2bqIIxJe+zKwfWcMMh24O0QZtYObbfmrGvemKv/FaZ/IhlxG9GntOI1VBjqh4xOdQR8IfPNnSP7iMTj2Q/oKZValRFAEI7Gn6b70mAIPfkktXAv1+AdoU33AVb5UZAtmhgiTylcZMdFstdhXxEgY85ei8q1SsM9718KEhys/azlOeVkNOuACmTAjp61umE32NITlS3eHpkOXM+R7H0AtCWymfmrtJ9jETg1lYArMUO6PdQUtumKm/DfY+nYUoQYdbNFPwqVZl22VK/ploDw9xzX80btd3YHtQkQyyesG5S/3XcxuYJGqkP4Qzs1iV518lA9uVCcGV4DG0Tg4nKc2eG3NqI3q08sPZdNSs=" -matrix: - include: - - jdk: openjdk9 - os: linux - env: JAVA_VERSION=9 - - jdk: openjdk10 - os: linux - env: JAVA_VERSION=10 - - jdk: openjdk11 - os: linux - env: JAVA_VERSION=11 - - jdk: openjdk17 - os: linux - env: JAVA_VERSION=17 -install: true -script: - - ./mvnw clean verify sonar:sonar -Dsonar.projectKey=alimate_errors-spring-boot-starter -B - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/README.md b/README.md index 901e8bd..0d3d641 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@

- + error-spring-boot-starter

Errors Spring Boot Starter

-[![Build Status](https://travis-ci.org/alimate/errors-spring-boot-starter.svg?branch=master)](https://travis-ci.org/alimate/errors-spring-boot-starter) -[![codecov](https://codecov.io/gh/alimate/errors-spring-boot-starter/branch/master/graph/badge.svg)](https://codecov.io/gh/alimate/errors-spring-boot-starter) -[![Maven Central](https://img.shields.io/maven-central/v/me.alidg/errors-spring-boot-starter.svg)](https://search.maven.org/search?q=g:me.alidg%20AND%20a:errors-spring-boot-starter) -[![Javadocs](http://www.javadoc.io/badge/me.alidg/errors-spring-boot-starter.svg)](http://www.javadoc.io/doc/me.alidg/errors-spring-boot-starter) -[![Sonatype](https://img.shields.io/static/v1.svg?label=snapshot&message=v1.5.0-SNAPSHOT&color=blueviolet)](https://oss.sonatype.org/service/local/artifact/maven/redirect?r=snapshots&g=me.alidg&a=errors-spring-boot-starter&v=1.5.0-SNAPSHOT&e=jar) -[![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/alimate_errors-spring-boot-starter?label=code%20quality&server=https%3A%2F%2Fsonarcloud.io)](https://sonarcloud.io/dashboard?id=alimate_errors-spring-boot-starter) +[![Build Status](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions) +[![codecov](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter/branch/master/graph/badge.svg)](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter) +[![Maven Central](https://img.shields.io/maven-central/v/sajjaadalipour/errors-spring-boot-starter.svg)](https://search.maven.org/search?q=g:me.alidg%20AND%20a:errors-spring-boot-starter) +[![Javadocs](http://www.javadoc.io/badge/sajjaadalipour/errors-spring-boot-starter.svg)](http://www.javadoc.io/doc/me.alidg/errors-spring-boot-starter) +[![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/sajjaadalipour_errors-spring-boot-starter?label=code%20quality&server=https%3A%2F%2Fsonarcloud.io)](https://sonarcloud.io/dashboard?id=sajjaadalipour_errors-spring-boot-starter) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

A Bountiful, Consistent and Opinionated Approach to Handle all sorts of Exceptions.

@@ -40,8 +39,6 @@ + [Logging Exceptions](#logging-exceptions) + [Post Processing Handled Exceptions](#post-processing-handled-exceptions) + [Registering Custom Handlers](#registering-custom-handlers) - + [Test Support](#test-support) - * [Appendix](#appendix) + [Configuration](#configuration) * [License](#license) @@ -62,7 +59,7 @@ Built on top of Spring Boot's great exception handling mechanism, the `errors-sp ### Download -Download the [latest JAR](https://search.maven.org/remotecontent?filepath=me/alidg/errors-spring-boot-starter/1.4.0/errors-spring-boot-starter-1.4.0.jar) or grab via Maven: +#### Compatible With Spring Boot 2.2.0.RELEASE and Java 8+ ```xml @@ -77,29 +74,20 @@ or Gradle: compile "me.alidg:errors-spring-boot-starter:1.4.0" ``` -If you like to stay at the cutting edge, use our `2.0.0-SNAPSHOT` version. Of course, you should define the following -snapshot repository: +#### Compatible With Spring Boot 3.2.0+ and Java 21 + ```xml - - - Sonatype - https://oss.sonatype.org/content/repositories/snapshots/ - - -``` -or: -```groovy -repositories { - maven { - url 'https://oss.sonatype.org/content/repositories/snapshots/' - } -} + + com.github.sajjaadalipour + errors-spring-boot-starter + 2.0.0 + ``` -### Prerequisites -The main dependency is JDK 8+. Tested with: - - JDK 9, JDK 10, and JDK 11 on Linux. - - Spring Boot `3.0.0+` +or Gradle: +``` +compile "com.github.sajjaadalipour:errors-spring-boot-starter:2.0.0" +``` ### Overview The `WebErrorHandler` implementations are responsible for handling different kinds of exceptions. When an exception @@ -391,56 +379,58 @@ common Spring Security exceptions: #### Reactive Security When the Spring Security is detected along with the Reactive stack, the starter registers two extra handlers to handle -all security related exceptions. In contrast with other handlers which register themselves automatically, in order to use these +all security-related exceptions. In contrast with other handlers which register themselves automatically, in order to use these two handlers, you should register them in your security configuration manually as follows: ```java +@Configuration +@EnableWebFlux @EnableWebFluxSecurity -public class WebFluxSecurityConfig { - - // other configurations +public class ReactiveConfig { @Bean - public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, - ServerAccessDeniedHandler accessDeniedHandler, - ServerAuthenticationEntryPoint authenticationEntryPoint) { - http - .csrf().accessDeniedHandler(accessDeniedHandler) - .and() - .exceptionHandling() - .accessDeniedHandler(accessDeniedHandler) - .authenticationEntryPoint(authenticationEntryPoint) - // other configurations - - return http.build(); + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, + ServerAccessDeniedHandler accessDeniedHandler, + ServerAuthenticationEntryPoint authenticationEntryPoint) { + return http + .csrf(ServerHttpSecurity.CsrfSpec::disable) + .exceptionHandling(configurer -> configurer + .authenticationEntryPoint(authenticationEntryPoint) + .accessDeniedHandler(accessDeniedHandler)) + .build(); } } + ``` The registered `ServerAccessDeniedHandler` and `ServerAuthenticationEntryPoint` are responsible for handling `AccessDeniedException` and `AuthenticationException` exceptions, respectively. #### Servlet Security When the Spring Security is detected along with the traditional servlet stack, the starter registers two extra handlers to handle -all security related exceptions. In contrast with other handlers which register themselves automatically, in order to use these +all security-related exceptions. In contrast with other handlers which register themselves automatically, in order to use these two handlers, you should register them in your security configuration manually as follows: ```java +@Configuration @EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { +@EnableMethodSecurity +public class ServletConfig { private final AccessDeniedHandler accessDeniedHandler; private final AuthenticationEntryPoint authenticationEntryPoint; - public SecurityConfig(AccessDeniedHandler accessDeniedHandler, AuthenticationEntryPoint authenticationEntryPoint) { + public ServletConfig(AccessDeniedHandler accessDeniedHandler, AuthenticationEntryPoint authenticationEntryPoint) { this.accessDeniedHandler = accessDeniedHandler; this.authenticationEntryPoint = authenticationEntryPoint; } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .exceptionHandling() - .accessDeniedHandler(accessDeniedHandler) - .authenticationEntryPoint(authenticationEntryPoint); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + return httpSecurity + .anonymous(AbstractHttpConfigurer::disable) + .csrf(AbstractHttpConfigurer::disable) + .exceptionHandling(configurer -> configurer.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)) + .build(); } + } ``` The registered `AccessDeniedHandler` and `AuthenticationEntryPoint` are responsible for handling `AccessDeniedException` @@ -461,8 +451,8 @@ By default, errors would manifest themselves in the HTTP response bodies with th #### Fingerprinting There is also an option to generate error `fingerprint`. Fingerprint is a unique hash of error -event which might be used as a correlation ID of error presented to user, and reported in -application backend (e.g. in detailed log message). To generate error fingerprints, add +event that might be used as a correlation ID of error presented to user, and reported in +application backend (e.g., in a detailed log message). To generate error fingerprints, add the configuration property `errors.add-fingerprint=true`. We provide two fingerprint providers implementations: @@ -581,49 +571,6 @@ If you're going to register multiple handlers, you can change their priority usi handlers would be registered after built-in exception handlers (Validation, `ExceptionMapping`, etc.). If you don't like this idea, provide a custom *Bean* of type `WebErrorHandlers` and the default one would be discarded. -### Test Support -To enable our test support for `WebMvcTest`s, just add the `@AutoConfigureErrors` annotation to your test -class. That's how a `WebMvcTest` would look like with errors support enabled: -```java -@AutoConfigureErrors -@RunWith(SpringRunner.class) -@WebMvcTest(UserController.class) -public class UserControllerIT { - - @Autowired private MockMvc mvc; - - @Test - public void createUser_ShouldReturnBadRequestForInvalidBodies() throws Exception { - mvc.perform(post("/users").content("{}")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.errors[0].code").value("username.required")); - } -} -``` -For `WebFluxTest`s, the test support is almost the same as the Servlet stack: -```java -@AutoConfigureErrors -@RunWith(SpringRunner.class) -@WebFluxTest(UserController.class) -@ImportAutoConfiguration(ErrorWebFluxAutoConfiguration.class) // Drop this if you're using Spring Boot 2.1.4+ -public class UserControllerIT { - - @Autowired private WebTestClient client; - - @Test - public void createUser_ShouldReturnBadRequestForInvalidBodies() { - client.post() - .uri("/users") - .syncBody("{}").header(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE) - .exchange() - .expectStatus().isBadRequest() - .expectBody().jsonPath("$.errors[0].code").isEqualTo("username.required"); - } -} -``` - -## Appendix - ### Configuration Additional configuration of this starter can be provided by configuration properties - the Spring Boot way. All configuration properties start with `errors`. Below is a list of supported properties: diff --git a/pom.xml b/pom.xml index a251442..26aecc1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,16 +5,16 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - me.alidg + com.github.sajjaadalipour errors-spring-boot-starter - 2.0.0-SNAPSHOT + 2.0.0 ${project.groupId}:${project.artifactId} A Spring Boot starter which provides a few primitives to handle exceptions more elegantly. - https://github.com/alimate/errors-spring-boot-starter + https://github.com/sajjaadalipour/errors-spring-boot-starter @@ -49,19 +49,17 @@ - scm:git:git://github.com/alimate/errors-spring-boot-starter.git - scm:git:ssh://github.com/alimate/errors-spring-boot-starter.git - https://github.com/alimate/errors-spring-boot-starter + scm:git:git://github.com/sajjaadalipour/errors-spring-boot-starter.git + scm:git:ssh://github.com/sajjaadalipour/errors-spring-boot-starter.git + + https://github.com/sajjaadalipour/errors-spring-boot-starter - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2 + github + GitHub sajjaadalipour Apache Maven Packages + https://maven.pkg.github.com/sajjaadalipour/errors-spring-boot-starter @@ -79,6 +77,8 @@ 3.0.1 1.6 6.2.0 + sajjaadalipour + https://sonarcloud.io @@ -310,45 +310,4 @@
- - - - - - CI - - - env.JAVA_VERSION - - - - ${env.JAVA_VERSION} - - - - - - release - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - - - From 3ac608650ab266c54f10b1232a7bc8af5173499c Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Wed, 13 Dec 2023 10:19:04 +0330 Subject: [PATCH 4/6] Fix sonar reported issues --- README.md | 4 +- pom.xml | 71 +++------------- .../attributes/ReactiveErrorAttributes.java | 5 +- .../ResponseStatusWebErrorHandler.java | 84 +++++++++---------- .../mvc/ErrorsControllerAdviceTest.java | 10 +-- 5 files changed, 59 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 0d3d641..87e6b3d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@

Errors Spring Boot Starter

-[![Build Status](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions) -[![codecov](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter/branch/master/graph/badge.svg)](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter) +[![Build Status](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/sajjaadalipour/errors-spring-boot-starter/actions) +[![codecov](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter/graph/badge.svg?token=LEBHWWZO97)](https://codecov.io/gh/sajjaadalipour/errors-spring-boot-starter) [![Maven Central](https://img.shields.io/maven-central/v/sajjaadalipour/errors-spring-boot-starter.svg)](https://search.maven.org/search?q=g:me.alidg%20AND%20a:errors-spring-boot-starter) [![Javadocs](http://www.javadoc.io/badge/sajjaadalipour/errors-spring-boot-starter.svg)](http://www.javadoc.io/doc/me.alidg/errors-spring-boot-starter) [![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/sajjaadalipour_errors-spring-boot-starter?label=code%20quality&server=https%3A%2F%2Fsonarcloud.io)](https://sonarcloud.io/dashboard?id=sajjaadalipour_errors-spring-boot-starter) diff --git a/pom.xml b/pom.xml index 26aecc1..6c2fa11 100644 --- a/pom.xml +++ b/pom.xml @@ -70,8 +70,7 @@ 1.1.1 0.8.11 3.8.1 - 2.22.0 - 2.22.0 + 2.19.1 ${maven-source-plugin.version} 3.0.2 3.0.1 @@ -170,70 +169,22 @@ org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} - - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} + + -Dspring.profiles.active=test + - it-tests integration-test - integration-test - verify - - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - default-prepare-agent - - prepare-agent - - - - default-prepare-agent-integration - - prepare-agent-integration - - - - merge-results - verify - - merge - - - - - ${project.build.directory} - - *.exec - - - - ${project.build.directory}/aggregate.exec - - - - post-merge-report - verify - - report + test - ${project.build.directory}/aggregate.exec - ${project.reporting.outputDirectory}/jacoco + + none + + + **/*IT.java + diff --git a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java index c9ccb4c..35bc2fe 100644 --- a/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java +++ b/src/main/java/me/alidg/errors/adapter/attributes/ReactiveErrorAttributes.java @@ -73,13 +73,12 @@ public Map getErrorAttributes(ServerRequest request, ErrorAttrib * Returns {@code true} if the given exception represents a not found kind of * {@link ResponseStatusException}. * - *

This is an attempt to handle not found exceptions in both stacks consistently. + *

This is an attempt to handle not-found exceptions in both stacks consistently. * * @param e The exception to examine. * @return {@code true} if it's not found one, {@code false} otherwise. */ private boolean isNotFoundException(Throwable e) { - return e instanceof ResponseStatusException && - ((ResponseStatusException) e).getStatusCode() == HttpStatus.NOT_FOUND; + return e instanceof ResponseStatusException exception && exception.getStatusCode() == HttpStatus.NOT_FOUND; } } diff --git a/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java b/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java index 200c297..ebe5ccb 100644 --- a/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java +++ b/src/main/java/me/alidg/errors/handlers/ResponseStatusWebErrorHandler.java @@ -64,49 +64,49 @@ public boolean canHandle(Throwable exception) { @NonNull @Override public HandledException handle(Throwable exception) { - if (exception instanceof UnsupportedMediaTypeStatusException e) { - Set types = getMediaTypes(e.getSupportedMediaTypes()); - Map> args = types.isEmpty() ? emptyMap() : argMap(NOT_SUPPORTED, arg("types", types)); - return new HandledException(NOT_SUPPORTED, UNSUPPORTED_MEDIA_TYPE, args); - } - - if (exception instanceof NotAcceptableStatusException e) { - Set types = getMediaTypes(e.getSupportedMediaTypes()); - Map> args = types.isEmpty() ? emptyMap() : argMap(NOT_ACCEPTABLE, arg("types", types)); - return new HandledException(NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE, args); - } - - if (exception instanceof MethodNotAllowedException e) { - String httpMethod = e.getHttpMethod(); - return new HandledException(METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED, argMap(METHOD_NOT_ALLOWED, arg("method", httpMethod))); - } - - if (exception instanceof WebExchangeBindException) { - return validationWebErrorHandler.handle(exception); - } - - if (exception instanceof ServerWebInputException e) { - MethodParameter parameter = e.getMethodParameter(); - if (exception.getCause() instanceof TypeMismatchException cause) { - if (cause.getPropertyName() == null) cause.initPropertyName(parameter.getParameterName()); - - return typeMismatchWebErrorHandler.handle(cause); - } - - HandledException handledException = handleMissingParameters(parameter); - if (handledException != null) return handledException; - - return new HandledException(INVALID_OR_MISSING_BODY, BAD_REQUEST, null); - } - - if (exception instanceof ResponseStatusException e) { - HttpStatus status = HttpStatus.valueOf(e.getStatusCode().value()); - if (status == NOT_FOUND) return new HandledException(NO_HANDLER, status, null); - - return new HandledException(UNKNOWN_ERROR_CODE, status, null); + if (exception == null) return new HandledException(UNKNOWN_ERROR_CODE, INTERNAL_SERVER_ERROR, null); + + switch (exception) { + case UnsupportedMediaTypeStatusException e: + return handleMediaBasedException(e.getSupportedMediaTypes(), NOT_SUPPORTED, UNSUPPORTED_MEDIA_TYPE); + + case NotAcceptableStatusException e: + return handleMediaBasedException(e.getSupportedMediaTypes(), NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE); + + case MethodNotAllowedException e: + return new HandledException(METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED, argMap(METHOD_NOT_ALLOWED, arg("method", e.getHttpMethod()))); + + case WebExchangeBindException e: + return validationWebErrorHandler.handle(exception); + + case ServerWebInputException e: + MethodParameter parameter = e.getMethodParameter(); + if (exception.getCause() instanceof TypeMismatchException cause) { + if (cause.getPropertyName() == null && parameter != null && parameter.getParameterName() != null) { + cause.initPropertyName(parameter.getParameterName()); + } + return typeMismatchWebErrorHandler.handle(cause); + } + HandledException handledException = handleMissingParameters(parameter); + if (handledException != null) return handledException; + return new HandledException(INVALID_OR_MISSING_BODY, BAD_REQUEST, null); + + case ResponseStatusException e: + HttpStatus status = HttpStatus.valueOf(e.getStatusCode().value()); + if (status == NOT_FOUND) { + return new HandledException(NO_HANDLER, status, null); + } + return new HandledException(UNKNOWN_ERROR_CODE, status, null); + + default: + return new HandledException(UNKNOWN_ERROR_CODE, INTERNAL_SERVER_ERROR, null); } + } - return new HandledException(UNKNOWN_ERROR_CODE, INTERNAL_SERVER_ERROR, null); + private HandledException handleMediaBasedException(List mediaTypes, String errorCode, HttpStatus statusCode) { + Set types = extractMediaTypes(mediaTypes); + Map> args = types.isEmpty() ? emptyMap() : argMap(errorCode, arg("types", types)); + return new HandledException(errorCode, statusCode, args); } /** @@ -181,7 +181,7 @@ private String extractParameterName(Annotation annotation, MethodParameter param return name.isEmpty() ? parameter.getParameterName() : name; } - private Set getMediaTypes(List mediaTypes) { + private Set extractMediaTypes(List mediaTypes) { if (mediaTypes == null) return emptySet(); return mediaTypes.stream().map(MediaType::toString).collect(toSet()); diff --git a/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java b/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java index 242877f..9132bae 100644 --- a/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java +++ b/src/test/java/me/alidg/errors/mvc/ErrorsControllerAdviceTest.java @@ -1,12 +1,8 @@ package me.alidg.errors.mvc; -import junitparams.JUnitParamsRunner; import me.alidg.errors.WebErrorHandlers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -15,12 +11,10 @@ * * @author Ali Dehghani */ -@RunWith(JUnitParamsRunner.class) -@ExtendWith(MockitoExtension.class) public class ErrorsControllerAdviceTest { @Test - public void constructor_WhenHandlerParamIsNull_ErrorMessageEqualToHandlerIsRequired() { + void constructor_WhenHandlerParamIsNull_ErrorMessageEqualToHandlerIsRequired() { assertThatThrownBy(() -> new ErrorsControllerAdvice(null, null) { }) .isInstanceOf(NullPointerException.class) @@ -28,7 +22,7 @@ public void constructor_WhenHandlerParamIsNull_ErrorMessageEqualToHandlerIsRequi } @Test - public void constructor_WhenAdapterParamIsNull_ErrorMessageEqualToAdapterIsRequired() { + void constructor_WhenAdapterParamIsNull_ErrorMessageEqualToAdapterIsRequired() { assertThatThrownBy(() -> new ErrorsControllerAdvice(Mockito.mock(WebErrorHandlers.class), null) { }) .isInstanceOf(NullPointerException.class) From 1a2ffb6a20b6b06684d9f6ed7ba4e724a46d38c9 Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Wed, 13 Dec 2023 11:32:18 +0330 Subject: [PATCH 5/6] Fix the jacoco plugin --- pom.xml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 6c2fa11..504209e 100644 --- a/pom.xml +++ b/pom.xml @@ -164,14 +164,10 @@ - org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} - - -Dspring.profiles.active=test - integration-test @@ -190,6 +186,26 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + org.apache.maven.plugins From 875992a0e867877359570ec185fc516654dae1c7 Mon Sep 17 00:00:00 2001 From: Sajjad Alipour Date: Wed, 13 Dec 2023 11:40:16 +0330 Subject: [PATCH 6/6] Update GitHub workflow --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8b74779..f04d999 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: Build on: push: branches: - - develop + - master jobs: build: