Skip to content

Commit

Permalink
fix: update GoogleJsonError object to accomodate all fields in Invali…
Browse files Browse the repository at this point in the history
…d parameter exception (#1783)

Fixes b/185405327

Invalid parameter error message does not contain details about which parameter is the invalid one. These fields have been added to GoogleJsonError object.
  • Loading branch information
Neenu1995 committed May 7, 2021
1 parent 96d3f9c commit 94c4570
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 16 deletions.
Expand Up @@ -21,6 +21,7 @@
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.util.Data;
import com.google.api.client.util.Key;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -183,6 +184,64 @@ public ErrorInfo clone() {
}
}

public static class Details {
@Key("@type")
private String type;

@Key private String detail;
@Key private List<ParameterViolations> parameterViolations;

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getDetail() {
return detail;
}

public void setDetail(String detail) {
this.detail = detail;
}

public List<ParameterViolations> getParameterViolations() {
return parameterViolations;
}

/**
* Sets parameterViolations list as immutable to prevent exposing mutable state.
*
* @param parameterViolations
*/
public void setParameterViolations(List<ParameterViolations> parameterViolations) {
this.parameterViolations = ImmutableList.copyOf(parameterViolations);
}
}

public static class ParameterViolations {
@Key private String parameter;
@Key private String description;

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getParameter() {
return parameter;
}

public void setParameter(String parameter) {
this.parameter = parameter;
}
}

/** List of detailed errors or {@code null} for none. */
@Key private List<ErrorInfo> errors;

Expand All @@ -192,6 +251,9 @@ public ErrorInfo clone() {
/** Human-readable explanation of the error or {@code null} for none. */
@Key private String message;

/** Lists type and parameterViolation details of an Exception */
@Key private List<Details> details;

/**
* Returns the list of detailed errors or {@code null} for none.
*
Expand All @@ -202,12 +264,13 @@ public final List<ErrorInfo> getErrors() {
}

/**
* Sets the list of detailed errors or {@code null} for none.
* Sets the list of detailed errors or {@code null} for none. Sets the list of detailed errors as
* immutable to prevent exposing mutable state.
*
* @since 1.8
*/
public final void setErrors(List<ErrorInfo> errors) {
this.errors = errors;
this.errors = ImmutableList.copyOf(errors);
}

/**
Expand Down Expand Up @@ -246,6 +309,20 @@ public final void setMessage(String message) {
this.message = message;
}

public List<Details> getDetails() {
return details;
}

/**
* Sets the list of invalid parameter error details as immutable to prevent exposing mutable
* state.
*
* @param details
*/
public void setDetails(List<Details> details) {
this.details = ImmutableList.copyOf(details);
}

@Override
public GoogleJsonError set(String fieldName, Object value) {
return (GoogleJsonError) super.set(fieldName, value);
Expand Down
Expand Up @@ -27,10 +27,11 @@
import com.google.api.client.testing.http.MockHttpTransport;
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
import java.io.InputStream;
import junit.framework.TestCase;

/**
* Tests {@link GoogleJsonError}.
* Tests {@link com.google.api.client.googleapis.json.GoogleJsonError}.
*
* @author Yaniv Inbar
*/
Expand All @@ -51,7 +52,8 @@ public class GoogleJsonErrorTest extends TestCase {
public void test_json() throws Exception {
JsonParser parser = FACTORY.createJsonParser(ERROR);
parser.nextToken();
GoogleJsonError e = parser.parse(GoogleJsonError.class);
com.google.api.client.googleapis.json.GoogleJsonError e =
parser.parse(com.google.api.client.googleapis.json.GoogleJsonError.class);
assertEquals(ERROR, FACTORY.toString(e));
}

Expand All @@ -70,6 +72,10 @@ static class ErrorTransport extends MockHttpTransport {
.setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
}

ErrorTransport(MockLowLevelHttpResponse mockLowLevelHttpResponse) {
response = mockLowLevelHttpResponse;
}

@Override
public LowLevelHttpRequest buildRequest(String name, String url) {
return new MockLowLevelHttpRequest(url).setResponse(response);
Expand All @@ -82,7 +88,41 @@ public void testParse() throws Exception {
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonError errorResponse = GoogleJsonError.parse(FACTORY, response);
com.google.api.client.googleapis.json.GoogleJsonError errorResponse =
com.google.api.client.googleapis.json.GoogleJsonError.parse(FACTORY, response);
assertEquals(ERROR, FACTORY.toString(errorResponse));
}

public void testParse_withDetails() throws Exception {
String DETAILS_ERROR =
"{"
+ "\"code\":400,"
+ "\"details\":[{"
+ "\"@type\":\"type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters\","
+ "\"parameterViolations\":[{"
+ "\"description\":\"Parameter didn't match regex '^[0-9a-zA-Z_]+$'\","
+ "\"parameter\":\"safeBrowsingApiKey\""
+ "}]},{"
+ "\"@type\":\"type.googleapis.com/google.rpc.DebugInfo\","
+ "\"detail\":\"test detail\"}],"
+ "\"message\":\"The template parameters are invalid.\","
+ "\"status\":\"INVALID_ARGUMENT\""
+ "}";
InputStream errorContent = GoogleJsonErrorTest.class.getResourceAsStream("error.json");
HttpTransport transport =
new ErrorTransport(
new MockLowLevelHttpResponse()
.setContent(errorContent)
.setContentType(Json.MEDIA_TYPE)
.setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN));
HttpRequest request =
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
com.google.api.client.googleapis.json.GoogleJsonError errorResponse =
com.google.api.client.googleapis.json.GoogleJsonError.parse(FACTORY, response);

assertEquals(DETAILS_ERROR, FACTORY.toString(errorResponse));
assertNotNull(errorResponse.getDetails());
}
}
Expand Up @@ -17,10 +17,13 @@
import com.google.api.client.googleapis.json.GoogleJsonErrorTest.ErrorTransport;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.Json;
import com.google.api.client.testing.http.HttpTesting;
import com.google.api.client.testing.http.MockHttpTransport;
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
import java.io.InputStream;
import junit.framework.TestCase;

/**
Expand All @@ -37,7 +40,8 @@ public void testFrom_noDetails() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("200"));
}
Expand All @@ -49,8 +53,12 @@ public void testFrom_withDetails() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
assertEquals(GoogleJsonErrorTest.ERROR, GoogleJsonErrorTest.FACTORY.toString(ge.getDetails()));
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertEquals(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.ERROR,
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY.toString(
ge.getDetails()));
assertTrue(ge.getMessage().startsWith("403"));
}

Expand All @@ -61,7 +69,8 @@ public void testFrom_detailsMissingContent() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
}
Expand All @@ -73,7 +82,8 @@ public void testFrom_detailsArbitraryJsonContent() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
}
Expand All @@ -85,7 +95,8 @@ public void testFrom_detailsArbitraryXmlContent() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
assertTrue(ge.getMessage().contains("<foo>"));
Expand All @@ -98,7 +109,8 @@ public void testFrom_errorNoContentButWithJsonContentType() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
}
Expand All @@ -110,7 +122,8 @@ public void testFrom_errorEmptyContentButWithJsonContentType() throws Exception
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
}
Expand All @@ -125,7 +138,8 @@ public void testFrom_detailsErrorObject() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNotNull(ge.getDetails());
assertEquals("invalid_token", ge.getDetails().getMessage());
assertTrue(ge.getMessage().contains("403"));
Expand All @@ -141,7 +155,8 @@ public void testFrom_detailsErrorString() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().contains("403"));
assertTrue(ge.getMessage().contains("invalid_token"));
Expand All @@ -155,8 +170,43 @@ public void testFrom_detailsNoErrorField() throws Exception {
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNull(ge.getDetails());
assertTrue(ge.getMessage().startsWith("403"));
}

public void testFrom_detailsWithInvalidParameter() throws Exception {
String DETAILS_ERROR =
"{"
+ "\"code\":400,"
+ "\"details\":[{"
+ "\"@type\":\"type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters\","
+ "\"parameterViolations\":[{"
+ "\"description\":\"Parameter didn't match regex '^[0-9a-zA-Z_]+$'\","
+ "\"parameter\":\"safeBrowsingApiKey\""
+ "}]},{"
+ "\"@type\":\"type.googleapis.com/google.rpc.DebugInfo\","
+ "\"detail\":\"test detail\"}],"
+ "\"message\":\"The template parameters are invalid.\","
+ "\"status\":\"INVALID_ARGUMENT\""
+ "}";
InputStream errorContent =
com.google.api.client.googleapis.json.GoogleJsonErrorTest.class.getResourceAsStream(
"error.json");
HttpTransport transport =
new ErrorTransport(
new MockLowLevelHttpResponse()
.setContent(errorContent)
.setContentType(Json.MEDIA_TYPE)
.setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN));
HttpRequest request =
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
GoogleJsonResponseException ge =
GoogleJsonResponseException.from(
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
assertNotNull(ge.getDetails().getDetails());
}
}
@@ -0,0 +1,23 @@
{
"error": {
"code": 400,
"message": "The template parameters are invalid.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters",
"parameterViolations": [
{
"parameter": "safeBrowsingApiKey",
"description": "Parameter didn't match regex '^[0-9a-zA-Z_]+$'"
}
]
},
{
"@type": "type.googleapis.com/google.rpc.DebugInfo",
"detail": "test detail"
}
]
}
}

0 comments on commit 94c4570

Please sign in to comment.