Skip to content

Commit 94c4570

Browse files
authored
fix: update GoogleJsonError object to accomodate all fields in Invalid 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.
1 parent 96d3f9c commit 94c4570

File tree

4 files changed

+206
-16
lines changed

4 files changed

+206
-16
lines changed

google-api-client/src/main/java/com/google/api/client/googleapis/json/GoogleJsonError.java

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.api.client.json.JsonObjectParser;
2222
import com.google.api.client.util.Data;
2323
import com.google.api.client.util.Key;
24+
import com.google.common.collect.ImmutableList;
2425
import java.io.IOException;
2526
import java.util.Collections;
2627
import java.util.List;
@@ -183,6 +184,64 @@ public ErrorInfo clone() {
183184
}
184185
}
185186

187+
public static class Details {
188+
@Key("@type")
189+
private String type;
190+
191+
@Key private String detail;
192+
@Key private List<ParameterViolations> parameterViolations;
193+
194+
public String getType() {
195+
return type;
196+
}
197+
198+
public void setType(String type) {
199+
this.type = type;
200+
}
201+
202+
public String getDetail() {
203+
return detail;
204+
}
205+
206+
public void setDetail(String detail) {
207+
this.detail = detail;
208+
}
209+
210+
public List<ParameterViolations> getParameterViolations() {
211+
return parameterViolations;
212+
}
213+
214+
/**
215+
* Sets parameterViolations list as immutable to prevent exposing mutable state.
216+
*
217+
* @param parameterViolations
218+
*/
219+
public void setParameterViolations(List<ParameterViolations> parameterViolations) {
220+
this.parameterViolations = ImmutableList.copyOf(parameterViolations);
221+
}
222+
}
223+
224+
public static class ParameterViolations {
225+
@Key private String parameter;
226+
@Key private String description;
227+
228+
public String getDescription() {
229+
return description;
230+
}
231+
232+
public void setDescription(String description) {
233+
this.description = description;
234+
}
235+
236+
public String getParameter() {
237+
return parameter;
238+
}
239+
240+
public void setParameter(String parameter) {
241+
this.parameter = parameter;
242+
}
243+
}
244+
186245
/** List of detailed errors or {@code null} for none. */
187246
@Key private List<ErrorInfo> errors;
188247

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

254+
/** Lists type and parameterViolation details of an Exception */
255+
@Key private List<Details> details;
256+
195257
/**
196258
* Returns the list of detailed errors or {@code null} for none.
197259
*
@@ -202,12 +264,13 @@ public final List<ErrorInfo> getErrors() {
202264
}
203265

204266
/**
205-
* Sets the list of detailed errors or {@code null} for none.
267+
* Sets the list of detailed errors or {@code null} for none. Sets the list of detailed errors as
268+
* immutable to prevent exposing mutable state.
206269
*
207270
* @since 1.8
208271
*/
209272
public final void setErrors(List<ErrorInfo> errors) {
210-
this.errors = errors;
273+
this.errors = ImmutableList.copyOf(errors);
211274
}
212275

213276
/**
@@ -246,6 +309,20 @@ public final void setMessage(String message) {
246309
this.message = message;
247310
}
248311

312+
public List<Details> getDetails() {
313+
return details;
314+
}
315+
316+
/**
317+
* Sets the list of invalid parameter error details as immutable to prevent exposing mutable
318+
* state.
319+
*
320+
* @param details
321+
*/
322+
public void setDetails(List<Details> details) {
323+
this.details = ImmutableList.copyOf(details);
324+
}
325+
249326
@Override
250327
public GoogleJsonError set(String fieldName, Object value) {
251328
return (GoogleJsonError) super.set(fieldName, value);

google-api-client/src/test/java/com/google/api/client/googleapis/json/GoogleJsonErrorTest.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@
2727
import com.google.api.client.testing.http.MockHttpTransport;
2828
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
2929
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
30+
import java.io.InputStream;
3031
import junit.framework.TestCase;
3132

3233
/**
33-
* Tests {@link GoogleJsonError}.
34+
* Tests {@link com.google.api.client.googleapis.json.GoogleJsonError}.
3435
*
3536
* @author Yaniv Inbar
3637
*/
@@ -51,7 +52,8 @@ public class GoogleJsonErrorTest extends TestCase {
5152
public void test_json() throws Exception {
5253
JsonParser parser = FACTORY.createJsonParser(ERROR);
5354
parser.nextToken();
54-
GoogleJsonError e = parser.parse(GoogleJsonError.class);
55+
com.google.api.client.googleapis.json.GoogleJsonError e =
56+
parser.parse(com.google.api.client.googleapis.json.GoogleJsonError.class);
5557
assertEquals(ERROR, FACTORY.toString(e));
5658
}
5759

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

75+
ErrorTransport(MockLowLevelHttpResponse mockLowLevelHttpResponse) {
76+
response = mockLowLevelHttpResponse;
77+
}
78+
7379
@Override
7480
public LowLevelHttpRequest buildRequest(String name, String url) {
7581
return new MockLowLevelHttpRequest(url).setResponse(response);
@@ -82,7 +88,41 @@ public void testParse() throws Exception {
8288
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
8389
request.setThrowExceptionOnExecuteError(false);
8490
HttpResponse response = request.execute();
85-
GoogleJsonError errorResponse = GoogleJsonError.parse(FACTORY, response);
91+
com.google.api.client.googleapis.json.GoogleJsonError errorResponse =
92+
com.google.api.client.googleapis.json.GoogleJsonError.parse(FACTORY, response);
8693
assertEquals(ERROR, FACTORY.toString(errorResponse));
8794
}
95+
96+
public void testParse_withDetails() throws Exception {
97+
String DETAILS_ERROR =
98+
"{"
99+
+ "\"code\":400,"
100+
+ "\"details\":[{"
101+
+ "\"@type\":\"type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters\","
102+
+ "\"parameterViolations\":[{"
103+
+ "\"description\":\"Parameter didn't match regex '^[0-9a-zA-Z_]+$'\","
104+
+ "\"parameter\":\"safeBrowsingApiKey\""
105+
+ "}]},{"
106+
+ "\"@type\":\"type.googleapis.com/google.rpc.DebugInfo\","
107+
+ "\"detail\":\"test detail\"}],"
108+
+ "\"message\":\"The template parameters are invalid.\","
109+
+ "\"status\":\"INVALID_ARGUMENT\""
110+
+ "}";
111+
InputStream errorContent = GoogleJsonErrorTest.class.getResourceAsStream("error.json");
112+
HttpTransport transport =
113+
new ErrorTransport(
114+
new MockLowLevelHttpResponse()
115+
.setContent(errorContent)
116+
.setContentType(Json.MEDIA_TYPE)
117+
.setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN));
118+
HttpRequest request =
119+
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
120+
request.setThrowExceptionOnExecuteError(false);
121+
HttpResponse response = request.execute();
122+
com.google.api.client.googleapis.json.GoogleJsonError errorResponse =
123+
com.google.api.client.googleapis.json.GoogleJsonError.parse(FACTORY, response);
124+
125+
assertEquals(DETAILS_ERROR, FACTORY.toString(errorResponse));
126+
assertNotNull(errorResponse.getDetails());
127+
}
88128
}

google-api-client/src/test/java/com/google/api/client/googleapis/json/GoogleJsonResponseExceptionTest.java

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
import com.google.api.client.googleapis.json.GoogleJsonErrorTest.ErrorTransport;
1818
import com.google.api.client.http.HttpRequest;
1919
import com.google.api.client.http.HttpResponse;
20+
import com.google.api.client.http.HttpStatusCodes;
2021
import com.google.api.client.http.HttpTransport;
2122
import com.google.api.client.json.Json;
2223
import com.google.api.client.testing.http.HttpTesting;
2324
import com.google.api.client.testing.http.MockHttpTransport;
25+
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
26+
import java.io.InputStream;
2427
import junit.framework.TestCase;
2528

2629
/**
@@ -37,7 +40,8 @@ public void testFrom_noDetails() throws Exception {
3740
request.setThrowExceptionOnExecuteError(false);
3841
HttpResponse response = request.execute();
3942
GoogleJsonResponseException ge =
40-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
43+
GoogleJsonResponseException.from(
44+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
4145
assertNull(ge.getDetails());
4246
assertTrue(ge.getMessage().startsWith("200"));
4347
}
@@ -49,8 +53,12 @@ public void testFrom_withDetails() throws Exception {
4953
request.setThrowExceptionOnExecuteError(false);
5054
HttpResponse response = request.execute();
5155
GoogleJsonResponseException ge =
52-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
53-
assertEquals(GoogleJsonErrorTest.ERROR, GoogleJsonErrorTest.FACTORY.toString(ge.getDetails()));
56+
GoogleJsonResponseException.from(
57+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
58+
assertEquals(
59+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.ERROR,
60+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY.toString(
61+
ge.getDetails()));
5462
assertTrue(ge.getMessage().startsWith("403"));
5563
}
5664

@@ -61,7 +69,8 @@ public void testFrom_detailsMissingContent() throws Exception {
6169
request.setThrowExceptionOnExecuteError(false);
6270
HttpResponse response = request.execute();
6371
GoogleJsonResponseException ge =
64-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
72+
GoogleJsonResponseException.from(
73+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
6574
assertNull(ge.getDetails());
6675
assertTrue(ge.getMessage().startsWith("403"));
6776
}
@@ -73,7 +82,8 @@ public void testFrom_detailsArbitraryJsonContent() throws Exception {
7382
request.setThrowExceptionOnExecuteError(false);
7483
HttpResponse response = request.execute();
7584
GoogleJsonResponseException ge =
76-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
85+
GoogleJsonResponseException.from(
86+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
7787
assertNull(ge.getDetails());
7888
assertTrue(ge.getMessage().startsWith("403"));
7989
}
@@ -85,7 +95,8 @@ public void testFrom_detailsArbitraryXmlContent() throws Exception {
8595
request.setThrowExceptionOnExecuteError(false);
8696
HttpResponse response = request.execute();
8797
GoogleJsonResponseException ge =
88-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
98+
GoogleJsonResponseException.from(
99+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
89100
assertNull(ge.getDetails());
90101
assertTrue(ge.getMessage().startsWith("403"));
91102
assertTrue(ge.getMessage().contains("<foo>"));
@@ -98,7 +109,8 @@ public void testFrom_errorNoContentButWithJsonContentType() throws Exception {
98109
request.setThrowExceptionOnExecuteError(false);
99110
HttpResponse response = request.execute();
100111
GoogleJsonResponseException ge =
101-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
112+
GoogleJsonResponseException.from(
113+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
102114
assertNull(ge.getDetails());
103115
assertTrue(ge.getMessage().startsWith("403"));
104116
}
@@ -110,7 +122,8 @@ public void testFrom_errorEmptyContentButWithJsonContentType() throws Exception
110122
request.setThrowExceptionOnExecuteError(false);
111123
HttpResponse response = request.execute();
112124
GoogleJsonResponseException ge =
113-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
125+
GoogleJsonResponseException.from(
126+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
114127
assertNull(ge.getDetails());
115128
assertTrue(ge.getMessage().startsWith("403"));
116129
}
@@ -125,7 +138,8 @@ public void testFrom_detailsErrorObject() throws Exception {
125138
request.setThrowExceptionOnExecuteError(false);
126139
HttpResponse response = request.execute();
127140
GoogleJsonResponseException ge =
128-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
141+
GoogleJsonResponseException.from(
142+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
129143
assertNotNull(ge.getDetails());
130144
assertEquals("invalid_token", ge.getDetails().getMessage());
131145
assertTrue(ge.getMessage().contains("403"));
@@ -141,7 +155,8 @@ public void testFrom_detailsErrorString() throws Exception {
141155
request.setThrowExceptionOnExecuteError(false);
142156
HttpResponse response = request.execute();
143157
GoogleJsonResponseException ge =
144-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
158+
GoogleJsonResponseException.from(
159+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
145160
assertNull(ge.getDetails());
146161
assertTrue(ge.getMessage().contains("403"));
147162
assertTrue(ge.getMessage().contains("invalid_token"));
@@ -155,8 +170,43 @@ public void testFrom_detailsNoErrorField() throws Exception {
155170
request.setThrowExceptionOnExecuteError(false);
156171
HttpResponse response = request.execute();
157172
GoogleJsonResponseException ge =
158-
GoogleJsonResponseException.from(GoogleJsonErrorTest.FACTORY, response);
173+
GoogleJsonResponseException.from(
174+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
159175
assertNull(ge.getDetails());
160176
assertTrue(ge.getMessage().startsWith("403"));
161177
}
178+
179+
public void testFrom_detailsWithInvalidParameter() throws Exception {
180+
String DETAILS_ERROR =
181+
"{"
182+
+ "\"code\":400,"
183+
+ "\"details\":[{"
184+
+ "\"@type\":\"type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters\","
185+
+ "\"parameterViolations\":[{"
186+
+ "\"description\":\"Parameter didn't match regex '^[0-9a-zA-Z_]+$'\","
187+
+ "\"parameter\":\"safeBrowsingApiKey\""
188+
+ "}]},{"
189+
+ "\"@type\":\"type.googleapis.com/google.rpc.DebugInfo\","
190+
+ "\"detail\":\"test detail\"}],"
191+
+ "\"message\":\"The template parameters are invalid.\","
192+
+ "\"status\":\"INVALID_ARGUMENT\""
193+
+ "}";
194+
InputStream errorContent =
195+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.class.getResourceAsStream(
196+
"error.json");
197+
HttpTransport transport =
198+
new ErrorTransport(
199+
new MockLowLevelHttpResponse()
200+
.setContent(errorContent)
201+
.setContentType(Json.MEDIA_TYPE)
202+
.setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN));
203+
HttpRequest request =
204+
transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL);
205+
request.setThrowExceptionOnExecuteError(false);
206+
HttpResponse response = request.execute();
207+
GoogleJsonResponseException ge =
208+
GoogleJsonResponseException.from(
209+
com.google.api.client.googleapis.json.GoogleJsonErrorTest.FACTORY, response);
210+
assertNotNull(ge.getDetails().getDetails());
211+
}
162212
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"error": {
3+
"code": 400,
4+
"message": "The template parameters are invalid.",
5+
"status": "INVALID_ARGUMENT",
6+
"details": [
7+
{
8+
"@type": "type.googleapis.com/google.dataflow.v1beta3.InvalidTemplateParameters",
9+
"parameterViolations": [
10+
{
11+
"parameter": "safeBrowsingApiKey",
12+
"description": "Parameter didn't match regex '^[0-9a-zA-Z_]+$'"
13+
}
14+
]
15+
},
16+
{
17+
"@type": "type.googleapis.com/google.rpc.DebugInfo",
18+
"detail": "test detail"
19+
}
20+
]
21+
}
22+
}
23+

0 commit comments

Comments
 (0)