Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring to allow fixing #1216 by making JsonApiException accept HTTP status code, propagate #1217

Merged
merged 20 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
549806a
Fixes #1216: change "unparseable" JSON into 400 from 200 (unprocessable)
tatu-at-datastax Jul 1, 2024
742eb6c
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 1, 2024
da82c47
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 1, 2024
23e2e02
Trying furiously get HTTP response 400 for low-level JSON parsing pro…
tatu-at-datastax Jul 2, 2024
a3964e9
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 3, 2024
95d7f70
Remove redundant mapping of Jackson exception from generic mapper
tatu-at-datastax Jul 3, 2024
aafed5b
Try to get http status code pass through
tatu-at-datastax Jul 3, 2024
e244a3e
More cleanup
tatu-at-datastax Jul 3, 2024
b1c5424
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 3, 2024
b2ea540
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 3, 2024
7056021
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 4, 2024
6fba977
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 8, 2024
e9e09b5
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 9, 2024
32edd84
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 10, 2024
159973e
Change IT to passing
tatu-at-datastax Jul 10, 2024
4e18d18
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 10, 2024
26c3ec0
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 11, 2024
112b413
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 11, 2024
b012c26
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 12, 2024
f466d23
Merge branch 'main' into tatu/1216-non-json-http-400
tatu-at-datastax Jul 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/java/io/stargate/sgv2/jsonapi/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.stargate.sgv2.jsonapi.exception;

import jakarta.ws.rs.core.Response;

/** ErrorCode is our internal enum that provides codes and a default message for that error code. */
public enum ErrorCode {
/** Command error codes. */
Expand Down Expand Up @@ -188,11 +190,21 @@ public JsonApiException toApiException(String format, Object... args) {
return new JsonApiException(this, message + ": " + String.format(format, args));
}

public JsonApiException toApiException(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factory method for passing HTTP status code along: needs to be the first argument due to varargs needed to pass format String arguments.

Response.Status httpStatus, String format, Object... args) {
return new JsonApiException(
this, message + ": " + String.format(format, args), null, httpStatus);
}

public JsonApiException toApiException(Throwable cause, String format, Object... args) {
return new JsonApiException(this, message + ": " + String.format(format, args), cause);
}

public JsonApiException toApiException() {
return new JsonApiException(this, message);
}

public JsonApiException toApiException(Response.Status httpStatus) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternate factory method when the default non-templatted ErrorCode message is used.

return new JsonApiException(this, message, null, httpStatus);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class JsonApiException extends RuntimeException implements Supplier<Comma

private final ErrorCode errorCode;

private final Response.Status httpStatus;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will try propagating HTTP response code along with the exception so it's neither lost nor need to be carried separately.


public JsonApiException(ErrorCode errorCode) {
this(errorCode, errorCode.getMessage(), null);
}
Expand All @@ -36,8 +38,14 @@ public JsonApiException(ErrorCode errorCode, Throwable cause) {
}

public JsonApiException(ErrorCode errorCode, String message, Throwable cause) {
this(errorCode, message, cause, Response.Status.OK);
}

public JsonApiException(
ErrorCode errorCode, String message, Throwable cause, Response.Status httpStatus) {
super(message, cause);
this.errorCode = errorCode;
this.httpStatus = httpStatus;
}

/** {@inheritDoc} */
Expand All @@ -50,7 +58,7 @@ public CommandResult get() {
}

// construct and return
CommandResult.Error error = getCommandResultError(message, Response.Status.OK);
CommandResult.Error error = getCommandResultError(message, httpStatus);

// handle cause as well
Throwable cause = getCause();
Expand Down Expand Up @@ -86,7 +94,15 @@ public CommandResult.Error getCommandResultError(Response.Status status) {
return getCommandResultError(getMessage(), status);
}

public CommandResult.Error getCommandResultError() {
return getCommandResultError(getMessage(), httpStatus);
}

public ErrorCode getErrorCode() {
return errorCode;
}

public Response.Status getHttpStatus() {
return httpStatus;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.stargate.sgv2.jsonapi.exception.mappers;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import org.jboss.resteasy.reactive.RestResponse;
Expand All @@ -12,7 +13,11 @@
public class GenericExceptionMapper {

// explicitly add types to override Quarkus mappers
@ServerExceptionMapper({Exception.class, MismatchedInputException.class})
@ServerExceptionMapper({
Exception.class,
JsonParseException.class,
MismatchedInputException.class
})
public RestResponse<CommandResult> genericExceptionMapper(Throwable e) {
CommandResult commandResult = new ThrowableCommandResultSupplier(e).get();
return commandResult.map();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public final class ThrowableToErrorMapper {
if (jae.getErrorCode().equals(ErrorCode.SERVER_EMBEDDING_GATEWAY_NOT_AVAILABLE)) {
return jae.getCommandResultError(message, Response.Status.INTERNAL_SERVER_ERROR);
}
return jae.getCommandResultError(message, Response.Status.OK);
return jae.getCommandResultError(message, jae.getHttpStatus());
}

// General Exception related to JSON handling, thrown by Jackson
Expand Down Expand Up @@ -188,8 +188,8 @@ private static CommandResult.Error handleAllNodesFailedException(
} else if (error instanceof DriverTimeoutException) {
// [data-api#1205] Need to map DriverTimeoutException as well
return ErrorCode.SERVER_DRIVER_TIMEOUT
.toApiException(message)
.getCommandResultError(Response.Status.INTERNAL_SERVER_ERROR);
.toApiException(Response.Status.INTERNAL_SERVER_ERROR, message)
.getCommandResultError();
}
}
}
Expand All @@ -200,11 +200,15 @@ private static CommandResult.Error handleAllNodesFailedException(

private static CommandResult.Error handleJsonProcessingException(
JacksonException e, String message) {
// Low-level parsing problem?
if (e instanceof JsonParseException) {
// Low-level parsing problem? Actual BAD_REQUEST (400) since we could not process
return ErrorCode.INVALID_REQUEST_NOT_JSON
.toApiException("underlying problem: (%s) %s", e.getClass().getName(), message)
.getCommandResultError(Response.Status.OK);
.toApiException(
Response.Status.BAD_REQUEST,
"underlying problem: (%s) %s",
e.getClass().getName(),
message)
.getCommandResultError();
}
// Unrecognized property? (note: CommandObjectMapperHandler handles some cases)
if (e instanceof UnrecognizedPropertyException upe) {
Expand All @@ -219,7 +223,7 @@ private static CommandResult.Error handleJsonProcessingException(
.toApiException(
"\"%s\" not one of known fields (%s) at '%s'",
upe.getPropertyName(), knownDesc, upe.getPathReference())
.getCommandResultError(Response.Status.OK);
.getCommandResultError();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this defaults to 200?

Copy link
Contributor Author

@tatu-at-datastax tatu-at-datastax Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; it uses Response.Status from JsonApiException, which in turn defaults to Response.Status.OK (unless constructor defines something else: in this case it's called via ErrorCode.toApiException() without specifying override).

Code is quite complicated with back and forth; here I just want to remove use of getCommandResultError() override and use one associated with JsonApiException

}

// Will need to add more handling but start with above
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public RestResponse<CommandResult> webApplicationExceptionMapper(WebApplicationE
null);
}
CommandResult commandResult = new ThrowableCommandResultSupplier(toReport).get();
if (toReport instanceof JsonApiException jae) {
return RestResponse.status(jae.getHttpStatus(), commandResult);
}
// Return 405 for method not allowed and 404 for not found
if (e instanceof NotAllowedException) {
return RestResponse.status(RestResponse.Status.METHOD_NOT_ALLOWED, commandResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public void malformedBody() {
.when()
.post(CollectionResource.BASE_PATH, namespaceName, collectionName)
.then()
// 10-Jul-2024, tatu: As per [data-api#1216], should be 400, not 200
// (we want to return 4xx because we cannot actually process the request
// as JSON is unparseable) but right now this is not working for some reason.
.statusCode(200)
.body("errors", hasSize(1))
.body("errors[0].errorCode", is("INVALID_REQUEST_NOT_JSON"))
Expand Down