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

feat: Error Details Improvement #1929

Merged
Expand Up @@ -16,6 +16,8 @@

package com.google.cloud.spanner;

import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ErrorDetails;
import com.google.cloud.grpc.BaseGrpcServiceException;
import com.google.common.base.Preconditions;
import com.google.protobuf.util.Durations;
Expand All @@ -24,6 +26,7 @@
import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.protobuf.ProtoUtils;
import java.util.Map;
import javax.annotation.Nullable;

/** Base exception type for all exceptions produced by the Cloud Spanner service. */
Expand Down Expand Up @@ -51,6 +54,7 @@ public String getResourceName() {
ProtoUtils.keyForProto(RetryInfo.getDefaultInstance());

private final ErrorCode code;
private final ApiException apiException;

/** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
SpannerException(
Expand All @@ -59,10 +63,23 @@ public String getResourceName() {
boolean retryable,
@Nullable String message,
@Nullable Throwable cause) {
super(message, cause, code.getCode(), retryable);
super(
message,
// If the cause is instance of APIException then using its cause to avoid the breaking
// change, because earlier we were passing APIException's cause to constructor.
cause instanceof ApiException ? cause.getCause() : cause,
gauravpurohit06 marked this conversation as resolved.
Show resolved Hide resolved
code.getCode(),
retryable);

if (token != DoNotConstructDirectly.ALLOWED) {
throw new AssertionError("Do not construct directly: use SpannerExceptionFactory");
}

if (cause instanceof ApiException) {
this.apiException = (ApiException) cause;
} else {
this.apiException = null;
}
this.code = Preconditions.checkNotNull(code);
}

Expand Down Expand Up @@ -95,4 +112,67 @@ static long extractRetryDelay(Throwable cause) {
}
return -1L;
}

/**
* Checks the underlying reason of the exception and if it's {@link ApiException} then return the
* reason otherwise null.
*
* @see <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L117">Reason</a>
* @return the reason of an error.
*/
public String getReason() {
gauravpurohit06 marked this conversation as resolved.
Show resolved Hide resolved
if (this.apiException != null) {
return this.apiException.getReason();
}
return null;
}

/**
* Checks the underlying reason of the exception and if it's {@link ApiException} then return the
* specific domain otherwise null.
*
* @see <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L125">Domain</a>
* @return the logical grouping to which the "reason" belongs.
*/
public String getDomain() {
if (this.apiException != null) {
return this.apiException.getDomain();
}
return null;
}

/**
* Checks the underlying reason of the exception and if it's {@link ApiException} then return a
* map of key-value pairs otherwise null.
*
* @see <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto#L135">Metadata</a>
* @return the map of additional structured details about an error.
*/
public Map<String, String> getMetadata() {
if (this.apiException != null) {
return this.apiException.getMetadata();
}
return null;
}

/**
* Checks the underlying reason of the exception and if it's {@link ApiException} then return the
* ErrorDetails otherwise null.
*
* @see <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto">Status</a>
* @see <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto">Error
* Details</a>
* @return An object containing getters for structured objects from error_details.proto.
*/
public ErrorDetails getErrorDetails() {
if (this.apiException != null) {
return this.apiException.getErrorDetails();
}
return null;
}
}
Expand Up @@ -295,12 +295,8 @@ private static SpannerException fromApiException(ApiException exception) {
code = Status.Code.UNKNOWN;
}
ErrorCode errorCode = ErrorCode.fromGrpcStatus(Status.fromCode(code));
if (exception.getCause() != null) {
return SpannerExceptionFactory.newSpannerException(
errorCode, exception.getMessage(), exception.getCause());
} else {
return SpannerExceptionFactory.newSpannerException(errorCode, exception.getMessage());
}
return SpannerExceptionFactory.newSpannerException(
gauravpurohit06 marked this conversation as resolved.
Show resolved Hide resolved
errorCode, exception.getMessage(), exception);
}

private static boolean isRetryable(ErrorCode code, @Nullable Throwable cause) {
Expand Down