Describe the bug
Our model includes a field which is a list of elements of another type. The children are stored inline as an array of objects, not in a separate DynamoDB table. This is breaking in iOS (Swift). See repro steps below
Amplify CLI Version
4.21.0 (ios)
To Reproduce
Add an api with the following schema:
type Todo @model {
id: ID!
name: String!
description: String
categories: [Category]
}
type Category {
name: ID!
color: String!
}
Fetch a Todo using the generated model:
Amplify.API.query(request: .get(Todo.self, byId: "........"))
Observed Behavior
The following error is returned:
[Amplify.GraphQLError(message: "Validation error of type SubSelectionRequired: Sub selection required for type null of field categories @ \'getTodo/categories\'"
Additional context
We have an Amplify environment running in production with such a schema and it is working well with a Javascript client. I am trying to add an iOS client and am running into this and other issues.
Things I've tried:
What can I do to work around this problem? Is there a way to run a custom query against AppSync and parse my own results, either with Amplify or another client? If not, is there another way to get an object from DyamoDB?
Thank you
@andriworld have you tried running the query on the console?
I just did some troubleshooting in the console.
This is the CORRECT query which works in the console, and is the one generated in the Javascript client (queries.js), as well as in the old API.swift from running "amplify codegen"
query GetTodo($id: ID!) {
getTodo(id: $id) {
__typename
id
name
description
categories {
__typename
name
color
}
createdAt
updatedAt }
}
It returns:
{
"data": {
"getTodo": {
"__typename": "Todo",
"id": "E5F117FB-194F-4D82-87FE-CCF007222BA8",
"name": "my first todo",
"description": "todo description",
"categories": [
{
"__typename": "Category",
"name": "urgent",
"color": "red"
},
{
"__typename": "Category",
"name": "info",
"color": "green"
}
],
"createdAt": "2020-05-29T17:35:13.536Z",
"updatedAt": "2020-05-29T17:35:13.536Z"
}
}
}
The new command "amplify codegen models" is building an INCORRECT query on the fly:
query GetTodo($id: ID!) {
getTodo(id: $id) {
id
categories
description
name
__typename
}
}
This last query gives the same error in the console:
{
"data": null,
"errors": [
{
"path": null,
"locations": [
{
"line": 4,
"column": 5,
"sourceName": null
}
],
"message": "Validation error of type SubSelectionRequired: Sub selection required for type null of field categories @ 'getTodo/categories'"
}
]
}
I have the same problem, on a similar schema.
Schema :
type LandmarkData
@model
@auth(rules: [ {allow: private, provider: userPools, operations: [ read ] } ])
{
id: ID!
name: String!
category: String
city: String
state: String
isFeatured: Boolean
isFavorite: Boolean
park: String
coordinates: CoordinateData
imageName: String
}
type CoordinateData {
longitude: Float
latitude: Float
}
Queries work in the AppSync console : (found in graphql/queries.graphql)
query ListLandmarks {
listLandmarkDatas {
items {
id
name
category
city
state
isFeatured
isFavorite
park
coordinates {
longitude
latitude
}
imageName
}
nextToken
}
}
Swift Code :
_ = Amplify.API.query(request: .list(LandmarkData.self)) { event in
switch event {
case .success(let result):
print("Landmarks query complete.")
switch result {
case .success(let landmarksData):
print("Successfully retrieved list of landmarks: \(landmarksData)")
case .failure(let error):
print("Can not retrieve result : error \(error.errorDescription)")
}
case .failure(let error):
print("Can not retrieve landmarks : error \(error)")
}
}
Result :
Can not retrieve result : error GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "Validation error of type SubSelectionRequired: Sub selection required for type CoordinateData of field coordinates @ \'listLandmarkDatas/items/coordinates\'", locations: Optional([Amplify.GraphQLError.Location(line: 7, column: 7)]), path: nil, extensions: nil)]
I just did some troubleshooting in the console.
This is the CORRECT query which works in the console, and is the one generated in the Javascript client (queries.js), as well as in the old API.swift from running "amplify codegen"
The difference is __typename ?
As a workaround, would it be possible to add the missing __typename in the queries ? I am not sure which file the new Amplify API Plugin uses to read the GraphQL query.
Here is the query sent by Amplify API Plugin (not working). It is captured from AppSyn's cloudwatch log
聽 | cecf5b00-1069-417f-8e65-03c1fee087da GraphQL Query: query ListLandmarkDatas($limit: Int) { listLandmarkDatas(limit: $limit) { items { id category city coordinates imageName isFavorite isFeatured name park state __typename } nextToken }}, Operation: null, Variables: { "limit": 1000 }
-- | --
Compared with the manual query I issued in the console, we can see the coordinate property is incorrect :
Working :
query ListLandmarks {
listLandmarkDatas {
items {
id
name
category
city
state
isFeatured
isFavorite
park
coordinates {
longitude
latitude
}
imageName
}
nextToken
}
}
Not Working :
query ListLandmarkDatas($limit: Int) {
listLandmarkDatas(limit: $limit) {
items {
id
category
city
coordinates
imageName
isFavorite
isFeatured
name
park
state
__typename
}
nextToken
}
}
I don't think this is a CLI codegen issue, I think this is an iOS Swift issue.
iOS lib dynamically generates the query document based on the model.
Somehow, the code at https://github.com/aws-amplify/amplify-ios/blob/master/AmplifyPlugins/Core/AWSPluginsCore/Model/Support/SelectionSet.swift#L38 does not recognize some attributes' type when the type is a non-model association
@sebsto yes you are seeing the same issue as I am. Note that after I hacked SelectionSet.swift so that the correct query went through, there were other issues decoding the results, as it's expecting a model. It seems the design doesn't account for non-model associations. In the meantime, I'm using AWSAppSyncClient for the API category.
Can you share your SelectionSet hack ? I am following the same path and try to modify SelectionSet but this is hardcoded for my Model, not a generic solution. (see this gist)
My hack unblocked me for queries I did not try mutation or anything fancy.
I reached the same conclusion as you, there is no support for non-model associations as of today. Not sure if this is by design or a miss.
I asked @lawmicha to take a look as he worked on that part of the code recently (https://github.com/aws-amplify/amplify-ios/pull/509)
@sebsto My hack was even more specific. Something like if field.isAssociationOwner || field.name == "categories". Good that you are unblocked. Even with the correct query, the decoding failed for me.
Actually your case has a single item of the nested type, whereas I have an array of items of the nested type.
just for reference https://github.com/aws-amplify/amplify-ios/pull/539
Test with libs 1.0.2 and CLI 4.22.0
Works for me.
@andriworld can you confirm this version fixes your problem too ?
It works!
Versions: @aws-amplify/[email protected], AmplifyPlugins/AWSAPIPlugin (1.0.3)
Steps: re-ran amplify codegen models and reimported into Xcode. Cleaned build folder
Schema: same as my original post, i.e. with nested array of non-model type
Output: Successfully retrieved todo: Todo(id: "E5F117FB-194F-4D82-87FE-CCF007222BA8", name: "my first todo", description: Optional("todo description"), categories: Optional([AmplifySandboxIOS.Category(name: "urgent", color: "red"), AmplifySandboxIOS.Category(name: "info", color: "green")]))
Thanks @lawmicha and @sebsto !