TSK-1682: Refactor TaskRestExceptionHandler to use a Map of ExceptionClass to HttpStatusCode

This commit is contained in:
Lia Lissmann 2021-09-02 11:46:37 +02:00 committed by LiaLissmann
parent aa725edb83
commit 816befbbdc
3 changed files with 88 additions and 67 deletions

View File

@ -40,16 +40,17 @@ Additionally, an optional set of message variables, containing some technical in
| *404 NOT_FOUND* | CLASSIFICATION_WITH_KEY_NOT_FOUND | classificationKey, domain
| *404 NOT_FOUND* | TASK_COMMENT_NOT_FOUND | taskCommentId
| *404 NOT_FOUND* | TASK_NOT_FOUND | taskId
| *404 NOT_FOUND* | USER_NOT_FOUND | userId
| *404 NOT_FOUND* | WORKBASKET_WITH_ID_NOT_FOUND | workbasketId
| *404 NOT_FOUND* | WORKBASKET_WITH_KEY_NOT_FOUND | workbasketKey, domain
| *404 NOT_FOUND* | USER_NOT_FOUND | userId
| *409 CONFLICT* | ATTACHMENT_ALREADY_EXISTS | attachmentId, taskId
| *409 CONFLICT* | CLASSIFICATION_ALREADY_EXISTS | classificationKey, domain
| *409 CONFLICT* | ENTITY_NOT_UP_TO_DATE | entityId
| *409 CONFLICT* | TASK_ALREADY_EXISTS | externalTaskId
| *409 CONFLICT* | USER_ALREADY_EXISTS | userID
| *409 CONFLICT* | WORKBASKET_ACCESS_ITEM_ALREADY_EXISTS | accessId, workbasketId
| *409 CONFLICT* | WORKBASKET_ALREADY_EXISTS | workbasketKey, domain
| *409 CONFLICT* | USER_ALREADY_EXISTS | userID
| *409 CONFLICT* | WORKBASKET_MARKED_FOR_DELETION | workbasketId
| *413 PAYLOAD_TOO_LARGE* | PAYLOAD_TOO_LARGE |
| *423 LOCKED* | CLASSIFICATION_IN_USE | classificationKey, domain
| *423 LOCKED* | WORKBASKET_IN_USE | workbasketId

View File

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@ -31,15 +32,20 @@ import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistExcep
import pro.taskana.classification.api.exceptions.ClassificationInUseException;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
import pro.taskana.common.api.exceptions.AutocommitFailedException;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.ConnectionNotSetException;
import pro.taskana.common.api.exceptions.DomainNotFoundException;
import pro.taskana.common.api.exceptions.ErrorCode;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.MismatchedRoleException;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
import pro.taskana.common.api.exceptions.UnsupportedDatabaseException;
import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException;
import pro.taskana.common.internal.util.MapCreator;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.common.rest.models.ExceptionRepresentationModel;
import pro.taskana.spi.history.api.exceptions.TaskanaHistoryEventNotFoundException;
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
@ -53,7 +59,6 @@ import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedToQueryWorkbasketException;
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
@ -67,52 +72,62 @@ public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler
public static final String ERROR_KEY_QUERY_MALFORMED = "QUERY_PARAMETER_MALFORMED";
public static final String ERROR_KEY_PAYLOAD = "PAYLOAD_TOO_LARGE";
@ExceptionHandler({
MismatchedRoleException.class,
MismatchedWorkbasketPermissionException.class,
MismatchedTaskCommentCreatorException.class,
NotAuthorizedToQueryWorkbasketException.class
})
protected ResponseEntity<Object> handleForbiddenExceptions(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.FORBIDDEN);
}
@ExceptionHandler({
ClassificationNotFoundException.class,
TaskCommentNotFoundException.class,
TaskNotFoundException.class,
TaskanaHistoryEventNotFoundException.class,
WorkbasketNotFoundException.class,
UserNotFoundException.class
})
protected ResponseEntity<Object> handleNotFound(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.NOT_FOUND);
}
@ExceptionHandler({
TaskAlreadyExistException.class,
ClassificationAlreadyExistException.class,
ConcurrencyException.class,
WorkbasketAlreadyExistException.class,
WorkbasketAccessItemAlreadyExistException.class,
UserAlreadyExistException.class,
AttachmentPersistenceException.class,
WorkbasketMarkedForDeletionException.class
})
protected ResponseEntity<Object> handleConflictExceptions(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler({WorkbasketInUseException.class, ClassificationInUseException.class})
protected ResponseEntity<Object> handleWorkbasketInUse(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.LOCKED);
}
public static final String ERROR_KEY_UNKNOWN_ERROR = "UNKNOWN_ERROR";
private static final Map<String, HttpStatus> HTTP_STATUS_BY_ERRORCODE_KEY =
Stream.of(
Pair.of(MalformedServiceLevelException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(WrongCustomHolidayFormatException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(DomainNotFoundException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(InvalidArgumentException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(ERROR_KEY_QUERY_MALFORMED, HttpStatus.BAD_REQUEST),
Pair.of(InvalidCallbackStateException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(InvalidOwnerException.ERROR_KEY, HttpStatus.BAD_REQUEST),
Pair.of(InvalidTaskStateException.ERROR_KEY, HttpStatus.BAD_REQUEST),
//
Pair.of(MismatchedRoleException.ERROR_KEY, HttpStatus.FORBIDDEN),
Pair.of(MismatchedTaskCommentCreatorException.ERROR_KEY, HttpStatus.FORBIDDEN),
Pair.of(MismatchedWorkbasketPermissionException.ERROR_KEY_ID, HttpStatus.FORBIDDEN),
Pair.of(
MismatchedWorkbasketPermissionException.ERROR_KEY_KEY_DOMAIN,
HttpStatus.FORBIDDEN),
//
Pair.of(ClassificationNotFoundException.ERROR_KEY_ID, HttpStatus.NOT_FOUND),
Pair.of(ClassificationNotFoundException.ERROR_KEY_KEY_DOMAIN, HttpStatus.NOT_FOUND),
Pair.of(TaskCommentNotFoundException.ERROR_KEY, HttpStatus.NOT_FOUND),
Pair.of(TaskNotFoundException.ERROR_KEY, HttpStatus.NOT_FOUND),
Pair.of(UserNotFoundException.ERROR_KEY, HttpStatus.NOT_FOUND),
Pair.of(WorkbasketNotFoundException.ERROR_KEY_ID, HttpStatus.NOT_FOUND),
Pair.of(WorkbasketNotFoundException.ERROR_KEY_KEY_DOMAIN, HttpStatus.NOT_FOUND),
Pair.of(TaskanaHistoryEventNotFoundException.ERROR_KEY, HttpStatus.NOT_FOUND),
//
Pair.of(AttachmentPersistenceException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(ClassificationAlreadyExistException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(ConcurrencyException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(TaskAlreadyExistException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(UserAlreadyExistException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(WorkbasketAccessItemAlreadyExistException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(WorkbasketAlreadyExistException.ERROR_KEY, HttpStatus.CONFLICT),
Pair.of(WorkbasketMarkedForDeletionException.ERROR_KEY, HttpStatus.CONFLICT),
//
Pair.of(ERROR_KEY_PAYLOAD, HttpStatus.PAYLOAD_TOO_LARGE),
//
Pair.of(ClassificationInUseException.ERROR_KEY, HttpStatus.LOCKED),
Pair.of(WorkbasketInUseException.ERROR_KEY, HttpStatus.LOCKED),
//
Pair.of(AutocommitFailedException.ERROR_KEY, HttpStatus.INTERNAL_SERVER_ERROR),
Pair.of(ConnectionNotSetException.ERROR_KEY, HttpStatus.INTERNAL_SERVER_ERROR),
Pair.of(SystemException.ERROR_KEY, HttpStatus.INTERNAL_SERVER_ERROR),
Pair.of(UnsupportedDatabaseException.ERROR_KEY, HttpStatus.INTERNAL_SERVER_ERROR),
Pair.of(ERROR_KEY_UNKNOWN_ERROR, HttpStatus.INTERNAL_SERVER_ERROR))
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
@ExceptionHandler(MaxUploadSizeExceededException.class)
protected ResponseEntity<Object> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex, WebRequest req) {
return buildResponse(ErrorCode.of(ERROR_KEY_PAYLOAD), ex, req, HttpStatus.PAYLOAD_TOO_LARGE);
HttpStatus status =
HTTP_STATUS_BY_ERRORCODE_KEY.getOrDefault(
ERROR_KEY_PAYLOAD, HttpStatus.INTERNAL_SERVER_ERROR);
return buildResponse(ErrorCode.of(ERROR_KEY_PAYLOAD), ex, req, status);
}
@ExceptionHandler(BeanInstantiationException.class)
@ -120,46 +135,43 @@ public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler
BeanInstantiationException ex, WebRequest req) {
if (ex.getCause() instanceof InvalidArgumentException) {
InvalidArgumentException cause = (InvalidArgumentException) ex.getCause();
return buildResponse(cause.getErrorCode(), ex, req, HttpStatus.BAD_REQUEST);
return handleTaskanaException(cause, req);
}
return buildResponse(null, ex, req, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler({
InvalidTaskStateException.class,
InvalidCallbackStateException.class,
InvalidOwnerException.class,
InvalidArgumentException.class,
DomainNotFoundException.class,
MalformedServiceLevelException.class,
WrongCustomHolidayFormatException.class
})
protected ResponseEntity<Object> handleBadRequestExceptions(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.BAD_REQUEST);
}
// This ExceptionHandler exists to convert IllegalArgumentExceptions to InvalidArgumentExceptions.
// Once IllegalArgumentExceptions are no longer in use, you can delete this \(*_*)/
@ExceptionHandler(IllegalArgumentException.class)
protected ResponseEntity<Object> handleIllegalArgumentException(
IllegalArgumentException ex, WebRequest req) {
return buildResponse(
ErrorCode.of(InvalidArgumentException.ERROR_KEY), ex, req, HttpStatus.BAD_REQUEST);
HttpStatus status =
HTTP_STATUS_BY_ERRORCODE_KEY.getOrDefault(
InvalidArgumentException.ERROR_KEY, HttpStatus.INTERNAL_SERVER_ERROR);
return buildResponse(ErrorCode.of(InvalidArgumentException.ERROR_KEY), ex, req, status);
}
@ExceptionHandler(TaskanaRuntimeException.class)
protected ResponseEntity<Object> handleUnknownTaskanaRuntimeExceptions(
protected ResponseEntity<Object> handleTaskanaRuntimeException(
TaskanaRuntimeException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.INTERNAL_SERVER_ERROR);
HttpStatus status =
HTTP_STATUS_BY_ERRORCODE_KEY.getOrDefault(
ex.getErrorCode().getKey(), HttpStatus.INTERNAL_SERVER_ERROR);
return buildResponse(ex.getErrorCode(), ex, req, status);
}
@ExceptionHandler(TaskanaException.class)
protected ResponseEntity<Object> handleUnknownTaskanaExceptions(
TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.INTERNAL_SERVER_ERROR);
protected ResponseEntity<Object> handleTaskanaException(TaskanaException ex, WebRequest req) {
HttpStatus status =
HTTP_STATUS_BY_ERRORCODE_KEY.getOrDefault(
ex.getErrorCode().getKey(), HttpStatus.INTERNAL_SERVER_ERROR);
return buildResponse(ex.getErrorCode(), ex, req, status);
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleGeneralException(Exception ex, WebRequest req) {
return buildResponse(ErrorCode.of("UNKNOWN_ERROR"), ex, req, HttpStatus.INTERNAL_SERVER_ERROR);
return buildResponse(
ErrorCode.of(ERROR_KEY_UNKNOWN_ERROR), ex, req, HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override

View File

@ -26,6 +26,8 @@ import pro.taskana.task.api.exceptions.MismatchedTaskCommentCreatorException;
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
@ -103,4 +105,10 @@ class ExceptionErrorKeyTest {
assertThat(TaskanaRestExceptionHandler.ERROR_KEY_QUERY_MALFORMED)
.isEqualTo("QUERY_PARAMETER_MALFORMED");
}
@Test
void should_ProvideConsistentErrorKey_For_UserExceptions() {
assertThat(UserNotFoundException.ERROR_KEY).isEqualTo("USER_NOT_FOUND");
assertThat(UserAlreadyExistException.ERROR_KEY).isEqualTo("USER_ALREADY_EXISTS");
}
}