Skip to content

Commit

Permalink
Add support for HTTP status code in exception. (#1019)
Browse files Browse the repository at this point in the history
Signed-off-by: Artur Souza <[email protected]>
  • Loading branch information
artursouza committed Feb 27, 2024
1 parent d2296fd commit 665059f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void invokeActorMethodError() {

assertThrowsDaprException(
"ERR_SOMETHING",
"ERR_SOMETHING: error message",
"ERR_SOMETHING: error message (HTTP status code: 404)",
() -> mono.block());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public void testInvokeException() throws Exception {
// TODO(artursouza): change this to INTERNAL once runtime is fixed.
assertEquals("UNKNOWN", exception.getErrorCode());
assertNotNull(exception.getMessage());
assertTrue(exception.getMessage().contains("Internal Server Error"));
assertTrue(exception.getMessage().contains("HTTP status code: 500"));
assertTrue(new String(exception.getPayload()).contains("Internal Server Error"));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void publishPubSubNotFound(boolean useGrpc) throws Exception {
} else {
assertThrowsDaprExceptionWithReason(
"ERR_PUBSUB_NOT_FOUND",
"ERR_PUBSUB_NOT_FOUND: pubsub unknown pubsub is not found",
"ERR_PUBSUB_NOT_FOUND: pubsub unknown pubsub is not found (HTTP status code: 404)",
"DAPR_PUBSUB_NOT_FOUND",
() -> client.publishEvent("unknown pubsub", "mytopic", "payload").block());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void getStateStoreNotFound() {
// DaprException is guaranteed in the Dapr SDK but getCause() is null in HTTP while present in GRPC implementation.
assertThrowsDaprException(
"ERR_STATE_STORE_NOT_FOUND",
"ERR_STATE_STORE_NOT_FOUND: state store unknown state store is not found",
"ERR_STATE_STORE_NOT_FOUND: state store unknown state store is not found (HTTP status code: 400)",
() -> daprClient.getState("unknown state store", new State(stateKey), byte[].class).block());
}

Expand All @@ -78,7 +78,7 @@ public void getStatesStoreNotFound() {
// DaprException is guaranteed in the Dapr SDK but getCause() is null in HTTP while present in GRPC implementation.
assertThrowsDaprException(
"ERR_STATE_STORE_NOT_FOUND",
"ERR_STATE_STORE_NOT_FOUND: state store unknown state store is not found",
"ERR_STATE_STORE_NOT_FOUND: state store unknown state store is not found (HTTP status code: 400)",
() -> daprClient.getBulkState(
"unknown state store",
Collections.singletonList(stateKey),
Expand Down
15 changes: 6 additions & 9 deletions sdk/src/main/java/io/dapr/client/DaprHttp.java
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ private static DaprError parseDaprError(byte[] json) {
try {
return DAPR_ERROR_DETAILS_OBJECT_MAPPER.readValue(json, DaprError.class);
} catch (IOException e) {
throw new DaprException("UNKNOWN", new String(json, StandardCharsets.UTF_8), json);
// Could not parse DaprError. Return null.
return null;
}
}

Expand Down Expand Up @@ -384,17 +385,13 @@ public void onResponse(@NotNull Call call, @NotNull okhttp3.Response response) t
try {
byte[] payload = getBodyBytesOrEmptyArray(response);
DaprError error = parseDaprError(payload);
if ((error != null) && (error.getErrorCode() != null)) {
if (error.getMessage() != null) {
future.completeExceptionally(new DaprException(error, payload));
} else {
future.completeExceptionally(
new DaprException(error.getErrorCode(), "HTTP status code: " + response.code(), payload));
}
if (error != null) {
future.completeExceptionally(new DaprException(error, payload, response.code()));
return;
}

future.completeExceptionally(new DaprException("UNKNOWN", "HTTP status code: " + response.code(), payload));
future.completeExceptionally(
new DaprException("UNKNOWN", "", payload, response.code()));
return;
} catch (DaprException e) {
future.completeExceptionally(e);
Expand Down
92 changes: 71 additions & 21 deletions sdk/src/main/java/io/dapr/exceptions/DaprException.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

package io.dapr.exceptions;

import com.google.rpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.protobuf.StatusProto;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -30,26 +32,32 @@ public class DaprException extends RuntimeException {
/**
* Dapr's error code for this exception.
*/
private String errorCode;
private final String errorCode;

/**
* The status details for the error.
*/
private DaprErrorDetails errorDetails;
private final DaprErrorDetails errorDetails;

/**
* Optional payload, if the exception came from a response body.
*/
private byte[] payload;
private final byte[] payload;

/**
* Optional HTTP status code, if error happened for an HTTP call (0 if not set).
*/
private final int httpStatusCode;

/**
* New exception from a server-side generated error code and message.
*
* @param daprError Server-side error.
* @param payload Payload containing the error.
* @param payload Optional payload containing the error.
* @param httpStatusCode Optional http Status Code (0 if not set).
*/
public DaprException(DaprError daprError, byte[] payload) {
this(daprError.getErrorCode(), daprError.getMessage(), daprError.getDetails(), payload);
public DaprException(DaprError daprError, byte[] payload, int httpStatusCode) {
this(daprError.getErrorCode(), daprError.getMessage(), daprError.getDetails(), payload, httpStatusCode);
}

/**
Expand Down Expand Up @@ -77,10 +85,11 @@ public DaprException(Throwable exception) {
*
* @param errorCode Client-side error code.
* @param message Client-side error message.
* @param payload Error's raw payload.
* @param payload Optional payload containing the error.
* @param httpStatusCode Optional http Status Code (0 if not set).
*/
public DaprException(String errorCode, String message, byte[] payload) {
this(errorCode, message, DaprErrorDetails.EMPTY_INSTANCE, payload);
public DaprException(String errorCode, String message, byte[] payload, int httpStatusCode) {
this(errorCode, message, DaprErrorDetails.EMPTY_INSTANCE, payload, httpStatusCode);
}

/**
Expand All @@ -89,10 +98,12 @@ public DaprException(String errorCode, String message, byte[] payload) {
* @param errorCode Client-side error code.
* @param message Client-side error message.
* @param errorDetails Details of the error from runtime.
* @param payload Payload containing the error.
* @param payload Optional payload containing the error.
* @param httpStatusCode Optional http Status Code (0 if not set).
*/
public DaprException(String errorCode, String message, List<Map<String, Object>> errorDetails, byte[] payload) {
this(errorCode, message, new DaprErrorDetails(errorDetails), payload);
public DaprException(
String errorCode, String message, List<Map<String, Object>> errorDetails, byte[] payload, int httpStatusCode) {
this(errorCode, message, new DaprErrorDetails(errorDetails), payload, httpStatusCode);
}

/**
Expand All @@ -101,10 +112,29 @@ public DaprException(String errorCode, String message, List<Map<String, Object>>
* @param errorCode Client-side error code.
* @param message Client-side error message.
* @param errorDetails Details of the error from runtime.
* @param payload Payload containing the error.
* @param payload Optional payload containing the error.
*/
public DaprException(String errorCode, String message, DaprErrorDetails errorDetails, byte[] payload) {
super(String.format("%s: %s", errorCode, message));
this(errorCode, message, errorDetails, payload, 0);
}

Check warning on line 119 in sdk/src/main/java/io/dapr/exceptions/DaprException.java

View check run for this annotation

Codecov / codecov/patch

sdk/src/main/java/io/dapr/exceptions/DaprException.java#L118-L119

Added lines #L118 - L119 were not covered by tests

/**
* New Exception from a client-side generated error code and message.
*
* @param errorCode Client-side error code.
* @param message Client-side error message.
* @param errorDetails Details of the error from runtime.
* @param payload Optional payload containing the error.
* @param httpStatusCode Optional http Status Code (0 if not set).
*/
public DaprException(
String errorCode,
String message,
DaprErrorDetails errorDetails,
byte[] payload,
int httpStatusCode) {
super(buildErrorMessage(errorCode, httpStatusCode, message));
this.httpStatusCode = httpStatusCode;
this.errorCode = errorCode;
this.errorDetails = errorDetails;
this.payload = payload;
Expand All @@ -120,8 +150,11 @@ public DaprException(String errorCode, String message, DaprErrorDetails errorDet
* unknown.)
*/
public DaprException(String errorCode, String message, Throwable cause) {
super(String.format("%s: %s", errorCode, emptyIfNull(message)), cause);
super(buildErrorMessage(errorCode, 0, message), cause);
this.httpStatusCode = 0;
this.errorCode = errorCode;
this.errorDetails = DaprErrorDetails.EMPTY_INSTANCE;
this.payload = null;
}

/**
Expand All @@ -137,7 +170,8 @@ public DaprException(String errorCode, String message, Throwable cause) {
*/
public DaprException(
String errorCode, String message, Throwable cause, DaprErrorDetails errorDetails, byte[] payload) {
super(String.format("%s: %s", errorCode, emptyIfNull(message)), cause);
super(buildErrorMessage(errorCode, 0, message), cause);
this.httpStatusCode = 0;
this.errorCode = errorCode;
this.errorDetails = errorDetails == null ? DaprErrorDetails.EMPTY_INSTANCE : errorDetails;
this.payload = payload;
Expand Down Expand Up @@ -170,6 +204,15 @@ public byte[] getPayload() {
return this.payload == null ? null : this.payload.clone();
}

/**
* Returns the exception's http status code, 0 if not applicable.
*
* @return Http status code (0 if not applicable).
*/
public int getHttpStatusCode() {
return this.httpStatusCode;
}

/**
* Wraps an exception into DaprException (if not already DaprException).
*
Expand Down Expand Up @@ -266,7 +309,7 @@ public static RuntimeException propagate(Throwable exception) {
while (e != null) {
if (e instanceof StatusRuntimeException) {
StatusRuntimeException statusRuntimeException = (StatusRuntimeException) e;
com.google.rpc.Status status = io.grpc.protobuf.StatusProto.fromThrowable(statusRuntimeException);
Status status = StatusProto.fromThrowable(statusRuntimeException);

DaprErrorDetails errorDetails = new DaprErrorDetails(status);

Expand All @@ -289,11 +332,18 @@ public static RuntimeException propagate(Throwable exception) {
return new DaprException(exception);
}

private static String emptyIfNull(String str) {
if (str == null) {
return "";
private static String buildErrorMessage(String errorCode, int httpStatusCode, String message) {
String result = ((errorCode == null) || errorCode.isEmpty()) ? "UNKNOWN: " : errorCode + ": ";
if ((message == null) || message.isEmpty()) {
if (httpStatusCode > 0) {
return result + "HTTP status code: " + httpStatusCode;
}
return result;
}

return str;
if (httpStatusCode > 0) {
return result + message + " (HTTP status code: " + httpStatusCode + ")";
}
return result + message;
}
}
11 changes: 7 additions & 4 deletions sdk/src/test/java/io/dapr/client/DaprClientHttpTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ public void invokeServiceDaprError() {
});

assertEquals("MYCODE", exception.getErrorCode());
assertEquals("MYCODE: My Message", exception.getMessage());
assertEquals("MYCODE: My Message (HTTP status code: 500)", exception.getMessage());
assertEquals(500, exception.getHttpStatusCode());
}

@Test
Expand Down Expand Up @@ -408,7 +409,7 @@ public void invokeServiceDaprErrorUnknownJSON() {
});

assertEquals("UNKNOWN", exception.getErrorCode());
assertEquals("UNKNOWN: { \"anything\": 7 }", exception.getMessage());
assertEquals("UNKNOWN: HTTP status code: 500", exception.getMessage());
assertEquals("{ \"anything\": 7 }", new String(exception.getPayload()));
}

Expand Down Expand Up @@ -1340,8 +1341,10 @@ public void getSecrets404WithErrorCode() {
"{\"errorCode\":\"ERR_SECRET_STORE_NOT_FOUND\"," +
"\"message\":\"error message\"}", MediaTypes.MEDIATYPE_JSON));

assertThrowsDaprException("ERR_SECRET_STORE_NOT_FOUND", "ERR_SECRET_STORE_NOT_FOUND: error message", () ->
daprClientHttp.getSecret(SECRET_STORE_NAME, "key").block()
assertThrowsDaprException(
"ERR_SECRET_STORE_NOT_FOUND",
"ERR_SECRET_STORE_NOT_FOUND: error message (HTTP status code: 404)",
() -> daprClientHttp.getSecret(SECRET_STORE_NAME, "key").block()
);
}

Expand Down

0 comments on commit 665059f

Please sign in to comment.