From 19b3f23fe0679d7398bb699898aec7e420c27c05 Mon Sep 17 00:00:00 2001 From: Ivan Senic Date: Tue, 18 Apr 2023 19:23:00 +0200 Subject: [PATCH] closes #350: errorCode and exceptionClass to be reported in errors (#369) --- .../jsonapi/exception/JsonApiException.java | 11 ++++++---- .../mappers/ThrowableToErrorMapper.java | 22 ++++++++++++++++--- .../sgv2/jsonapi/util/ExceptionUtil.java | 18 ++++++--------- .../jsonapi/api/v1/FindIntegrationTest.java | 9 +++++--- .../api/v1/FindOneIntegrationTest.java | 15 ++++++++----- .../exception/JsonApiExceptionTest.java | 15 ++++++++----- 6 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/exception/JsonApiException.java b/src/main/java/io/stargate/sgv2/jsonapi/exception/JsonApiException.java index b06d7059de..878c7dfc81 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/exception/JsonApiException.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/exception/JsonApiException.java @@ -43,11 +43,8 @@ public CommandResult get() { message = errorCode.getMessage(); } - // add error code as error field - Map fields = Map.of("errorCode", errorCode.name()); - // construct and return - CommandResult.Error error = new CommandResult.Error(message, fields); + CommandResult.Error error = getCommandResultError(message); // handle cause as well Throwable cause = getCause(); @@ -59,6 +56,12 @@ public CommandResult get() { } } + public CommandResult.Error getCommandResultError(String message) { + Map fields = + Map.of("errorCode", errorCode.name(), "exceptionClass", this.getClass().getSimpleName()); + return new CommandResult.Error(message, fields); + } + public ErrorCode getErrorCode() { return errorCode; } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/exception/mappers/ThrowableToErrorMapper.java b/src/main/java/io/stargate/sgv2/jsonapi/exception/mappers/ThrowableToErrorMapper.java index 5a135ebffa..35440515bc 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/exception/mappers/ThrowableToErrorMapper.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/exception/mappers/ThrowableToErrorMapper.java @@ -1,7 +1,9 @@ package io.stargate.sgv2.jsonapi.exception.mappers; import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; +import io.stargate.sgv2.jsonapi.exception.JsonApiException; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -10,6 +12,18 @@ */ public final class ThrowableToErrorMapper { + private static final BiFunction MAPPER_WITH_MESSAGE = + (throwable, message) -> { + // if our own exception, shortcut + if (throwable instanceof JsonApiException jae) { + return jae.getCommandResultError(message); + } + + // add error code as error field + Map fields = Map.of("exceptionClass", throwable.getClass().getSimpleName()); + return new CommandResult.Error(message, fields); + }; + private static final Function MAPPER = throwable -> { String message = throwable.getMessage(); @@ -17,9 +31,7 @@ public final class ThrowableToErrorMapper { message = "Unexpected exception occurred."; } - // add error code as error field - Map fields = Map.of("exceptionClass", throwable.getClass().getSimpleName()); - return new CommandResult.Error(message, fields); + return MAPPER_WITH_MESSAGE.apply(throwable, message); }; private ThrowableToErrorMapper() {} @@ -27,4 +39,8 @@ private ThrowableToErrorMapper() {} public static Function getMapperFunction() { return MAPPER; } + + public static BiFunction getMapperWithMessageFunction() { + return MAPPER_WITH_MESSAGE; + } } diff --git a/src/main/java/io/stargate/sgv2/jsonapi/util/ExceptionUtil.java b/src/main/java/io/stargate/sgv2/jsonapi/util/ExceptionUtil.java index 62ebaab88a..3aac7cd1b9 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/util/ExceptionUtil.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/util/ExceptionUtil.java @@ -2,26 +2,22 @@ import io.stargate.sgv2.jsonapi.api.model.command.CommandResult; import io.stargate.sgv2.jsonapi.exception.JsonApiException; +import io.stargate.sgv2.jsonapi.exception.mappers.ThrowableToErrorMapper; import io.stargate.sgv2.jsonapi.service.shredding.model.DocumentId; -import java.util.HashMap; import java.util.List; -import java.util.Map; public class ExceptionUtil { public static String getThrowableGroupingKey(Throwable error) { - String key = error.getClass().getSimpleName(); - if (error instanceof JsonApiException jae) key = jae.getErrorCode().name(); - return key; + if (error instanceof JsonApiException jae) { + return jae.getErrorCode().name(); + } else { + return error.getClass().getSimpleName(); + } } public static CommandResult.Error getError( String messageTemplate, List documentIds, Throwable throwable) { String message = messageTemplate.formatted(documentIds, throwable.getMessage()); - Map fields = new HashMap<>(); - fields.put("exceptionClass", throwable.getClass().getSimpleName()); - if (throwable instanceof JsonApiException jae) { - fields.put("errorCode", jae.getErrorCode().name()); - } - return new CommandResult.Error(message, fields); + return ThrowableToErrorMapper.getMapperWithMessageFunction().apply(throwable, message); } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java index 2ead771020..8e9335aff0 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindIntegrationTest.java @@ -325,7 +325,8 @@ public void inConditionNonArrayArray() { .statusCode(200) .body("errors", is(notNullValue())) .body("errors[1].message", is("$in operator must have `ARRAY`")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -348,7 +349,8 @@ public void inConditionNonIdField() { .statusCode(200) .body("errors", is(notNullValue())) .body("errors[1].message", is("Can use $in operator only on _id field")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -537,7 +539,8 @@ public void withExistFalseOperator() { .then() .statusCode(200) .body("errors[1].message", is("$exists operator supports only true")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java index 1de08c76d6..f0f5f5d682 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindOneIntegrationTest.java @@ -331,7 +331,8 @@ public void inConditionNonArrayArray() { .statusCode(200) .body("errors", is(notNullValue())) .body("errors[1].message", is("$in operator must have `ARRAY`")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -354,7 +355,8 @@ public void inConditionNonIdField() { .statusCode(200) .body("errors", is(notNullValue())) .body("errors[1].message", is("Can use $in operator only on _id field")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -536,7 +538,8 @@ public void withExistsOperatorFalse() { .body("data", is(nullValue())) .body("status", is(nullValue())) .body("errors[1].message", is("$exists operator supports only true")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -636,7 +639,8 @@ public void withAllOperatorNotArray() { .body("data", is(nullValue())) .body("status", is(nullValue())) .body("errors[1].message", is("$all operator must have `ARRAY` value")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } @Test @@ -712,7 +716,8 @@ public void withSizeOperatorNotNumber() { .body("data", is(nullValue())) .body("status", is(nullValue())) .body("errors[1].message", is("$size operator must have integer")) - .body("errors[1].exceptionClass", is("JsonApiException")); + .body("errors[1].exceptionClass", is("JsonApiException")) + .body("errors[1].errorCode", is("INVALID_FILTER_EXPRESSION")); } } } diff --git a/src/test/java/io/stargate/sgv2/jsonapi/exception/JsonApiExceptionTest.java b/src/test/java/io/stargate/sgv2/jsonapi/exception/JsonApiExceptionTest.java index a130fbf669..c5d8bc56b0 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/exception/JsonApiExceptionTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/exception/JsonApiExceptionTest.java @@ -25,8 +25,9 @@ public void happyPath() { error -> { assertThat(error.message()).isEqualTo("The provided command is not implemented."); assertThat(error.fields()) - .hasSize(1) - .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED"); + .hasSize(2) + .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED") + .containsEntry("exceptionClass", "JsonApiException"); }); } @@ -46,8 +47,9 @@ public void withCustomMessage() { error -> { assertThat(error.message()).isEqualTo("Custom message is more important."); assertThat(error.fields()) - .hasSize(1) - .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED"); + .hasSize(2) + .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED") + .containsEntry("exceptionClass", "JsonApiException"); }); } @@ -66,8 +68,9 @@ public void withCause() { error -> { assertThat(error.message()).isEqualTo("The provided command is not implemented."); assertThat(error.fields()) - .hasSize(1) - .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED"); + .hasSize(2) + .containsEntry("errorCode", "COMMAND_NOT_IMPLEMENTED") + .containsEntry("exceptionClass", "JsonApiException"); }) .anySatisfy( error -> {