Skip to content

Commit 0d08ad2

Browse files
feat(api) response deserialization should only skip top level for specific response types (#1062)
* feat(api) response deserialization should only skip top level for specific response types * Add null check back in
1 parent 54646bd commit 0d08ad2

File tree

4 files changed

+48
-159
lines changed

4 files changed

+48
-159
lines changed

aws-api-appsync/src/main/java/com/amplifyframework/api/graphql/GsonResponseAdapters.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
package com.amplifyframework.api.graphql;
1717

18+
import com.amplifyframework.core.model.Model;
19+
import com.amplifyframework.datastore.appsync.ModelWithMetadata;
1820
import com.amplifyframework.util.GsonObjectConverter;
1921
import com.amplifyframework.util.TypeMaker;
2022

@@ -72,20 +74,24 @@ public GraphQLResponse<Object> deserialize(JsonElement json, Type typeOfT, JsonD
7274
JsonElement jsonErrors = null;
7375

7476
if (jsonObject.has(DATA_KEY)) {
75-
jsonData = skipQueryLevel(jsonObject.get(DATA_KEY));
77+
jsonData = jsonObject.get(DATA_KEY);
7678
}
79+
7780
if (jsonObject.has(ERRORS_KEY)) {
7881
jsonErrors = jsonObject.get(ERRORS_KEY);
7982
}
8083

8184
List<GraphQLResponse.Error> errors = parseErrors(jsonErrors, context);
8285

8386
if (!(typeOfT instanceof ParameterizedType)) {
84-
throw new JsonParseException("Expected a parameterized type during list deserialization.");
87+
throw new JsonParseException("Expected a parameterized type during GraphQLResponse deserialization.");
8588
}
8689

8790
// Because typeOfT is ParameterizedType we can be sure this is a safe cast.
8891
final Type templateClassType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
92+
if (shouldSkipQueryLevel(templateClassType)) {
93+
jsonData = skipQueryLevel(jsonData);
94+
}
8995

9096
if (jsonData == null || jsonData.isJsonNull()) {
9197
return new GraphQLResponse<>(null, errors);
@@ -95,6 +101,24 @@ public GraphQLResponse<Object> deserialize(JsonElement json, Type typeOfT, JsonD
95101
}
96102
}
97103

104+
private boolean shouldSkipQueryLevel(Type type) {
105+
if (type instanceof ParameterizedType) {
106+
final Type rawType = ((ParameterizedType) type).getRawType();
107+
if (ModelWithMetadata.class.equals(rawType)) {
108+
return true;
109+
}
110+
if (Iterable.class.isAssignableFrom((Class<?>) rawType)) {
111+
return true;
112+
}
113+
} else {
114+
if (Model.class.isAssignableFrom((Class<?>) type)) {
115+
return true;
116+
}
117+
}
118+
119+
return false;
120+
}
121+
98122
// Skips a JSON level to get content of query, not query itself
99123
private JsonElement skipQueryLevel(JsonElement jsonData) throws JsonParseException {
100124
if (jsonData == null || jsonData.isJsonNull()) {

aws-api/src/test/java/com/amplifyframework/api/aws/GsonGraphQLResponseFactoryTest.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ public void nullDataNullErrorsReturnsEmptyResponseObject() throws ApiException {
8080
Resources.readAsString("null-gql-response.json");
8181

8282
// Act! Parse it into a model.
83-
84-
final GraphQLResponse<ListTodosResult> response =
85-
responseFactory.buildResponse(null, nullResponseJson, ListTodosResult.class);
83+
Type responseType = TypeMaker.getParameterizedType(PaginatedResult.class, Todo.class);
84+
final GraphQLResponse<PaginatedResult<Todo>> response =
85+
responseFactory.buildResponse(null, nullResponseJson, responseType);
8686

8787
// Assert that the model is constructed without content
8888
assertNotNull(response);
@@ -104,9 +104,10 @@ public void partialResponseRendersWithTodoDataAndErrors() throws ApiException {
104104
Resources.readAsString("partial-gql-response.json");
105105

106106
// Act! Parse it into a model.
107-
GraphQLRequest<ListTodosResult> request = buildDummyRequest(ListTodosResult.class);
108-
final GraphQLResponse<ListTodosResult> response =
109-
responseFactory.buildResponse(request, partialResponseJson, ListTodosResult.class);
107+
Type responseType = TypeMaker.getParameterizedType(PaginatedResult.class, Todo.class);
108+
GraphQLRequest<PaginatedResult<Todo>> request = buildDummyRequest(responseType);
109+
final GraphQLResponse<PaginatedResult<Todo>> response =
110+
responseFactory.buildResponse(request, partialResponseJson, responseType);
110111

111112
// Assert that the model contained things...
112113
assertNotNull(response);
@@ -116,7 +117,7 @@ public void partialResponseRendersWithTodoDataAndErrors() throws ApiException {
116117
// Assert that all of the fields of the different todos
117118
// match what we would expect from a manual inspection of the
118119
// JSON.
119-
final List<Todo> actualTodos = response.getData().getItems();
120+
final Iterable<Todo> actualTodos = response.getData().getItems();
120121

121122
final List<Todo> expectedTodos = Arrays.asList(
122123
Todo.builder()
@@ -246,8 +247,9 @@ public void errorResponseDeserializesExtensionsMap() throws ApiException {
246247
Resources.readAsString("error-extensions-gql-response.json");
247248

248249
// Act! Parse it into a model.
249-
final GraphQLResponse<ListTodosResult> response =
250-
responseFactory.buildResponse(null, partialResponseJson, ListTodosResult.class);
250+
Type responseType = TypeMaker.getParameterizedType(PaginatedResult.class, Todo.class);
251+
final GraphQLResponse<PaginatedResult<Todo>> response =
252+
responseFactory.buildResponse(null, partialResponseJson, responseType);
251253

252254
// Build the expected response.
253255
String message = "Conflict resolver rejects mutation.";
@@ -273,7 +275,7 @@ public void errorResponseDeserializesExtensionsMap() throws ApiException {
273275
extensions.put("data", data);
274276

275277
GraphQLResponse.Error expectedError = new GraphQLResponse.Error(message, locations, path, extensions);
276-
GraphQLResponse<ListTodosResult> expectedResponse = new GraphQLResponse<>(null,
278+
GraphQLResponse<PaginatedResult<Todo>> expectedResponse = new GraphQLResponse<>(null,
277279
Arrays.asList(expectedError, expectedError, expectedError, expectedError));
278280

279281
// Assert that the response is expected
@@ -295,8 +297,9 @@ public void errorWithNullFieldsCanBeParsed() throws ApiException {
295297
final String responseJson = Resources.readAsString("error-null-properties.json");
296298

297299
// Act! Parse it into a model.
298-
final GraphQLResponse<ListTodosResult> response =
299-
responseFactory.buildResponse(null, responseJson, ListTodosResult.class);
300+
Type responseType = TypeMaker.getParameterizedType(PaginatedResult.class, Todo.class);
301+
final GraphQLResponse<PaginatedResult<Todo>> response =
302+
responseFactory.buildResponse(null, responseJson, responseType);
300303

301304
// Build the expected response.
302305
Map<String, Object> extensions = new HashMap<>();
@@ -306,7 +309,7 @@ public void errorWithNullFieldsCanBeParsed() throws ApiException {
306309

307310
GraphQLResponse.Error expectedError =
308311
new GraphQLResponse.Error("the message", null, null, extensions);
309-
GraphQLResponse<ListTodosResult> expectedResponse =
312+
GraphQLResponse<PaginatedResult<Todo>> expectedResponse =
310313
new GraphQLResponse<>(null, Collections.singletonList(expectedError));
311314

312315
// Assert that the response is expected
@@ -332,7 +335,7 @@ public void partialResponseCanBeRenderedAsStringType() throws JSONException, Api
332335

333336
// Assert that the response data is just the data block as a JSON string
334337
assertEquals(
335-
partialResponseJson.getJSONObject("data").getJSONObject("listTodos").toString(),
338+
partialResponseJson.getJSONObject("data").toString(),
336339
response.getData()
337340
);
338341
}
@@ -428,10 +431,11 @@ public void awsDateTypesCanBeDeserialized() throws ApiException {
428431
// Act
429432
final String responseString = Resources.readAsString("list-meetings-response.json");
430433

431-
GraphQLRequest<ListMeetingsResult> request = buildDummyRequest(ListMeetingsResult.class);
432-
final GraphQLResponse<ListMeetingsResult> response =
433-
responseFactory.buildResponse(request, responseString, ListMeetingsResult.class);
434-
final List<Meeting> actualMeetings = response.getData().getItems();
434+
Type responseType = TypeMaker.getParameterizedType(PaginatedResult.class, Meeting.class);
435+
GraphQLRequest<PaginatedResult<Meeting>> request = buildDummyRequest(responseType);
436+
final GraphQLResponse<PaginatedResult<Meeting>> response =
437+
responseFactory.buildResponse(request, responseString, responseType);
438+
final Iterable<Meeting> actualMeetings = response.getData().getItems();
435439

436440
// Assert
437441
assertEquals(expectedMeetings, actualMeetings);

aws-api/src/test/java/com/amplifyframework/api/aws/ListMeetingsResult.java

Lines changed: 0 additions & 74 deletions
This file was deleted.

aws-api/src/test/java/com/amplifyframework/api/aws/ListTodosResult.java

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)