Amplify-ios: GraphQL query for model with list attribute gives error: "Sub selection required"

Created on 29 May 2020  路  12Comments  路  Source: aws-amplify/amplify-ios

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:

  • I added @model and @connection directives hoping it will fix the query. The operation returned successfully but the field was nil, as it was not being included in the document's SelectionSet
  • I forced the field to be included in the SelectionSet by hard-coding it in SelectionSet.swift. The query was correct and the server returned the list values, but the decoding failed as it was expecting non-existent fields Category.todoCategoriesId and Category.id

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

api bug closing soon datastore

All 12 comments

@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.

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 !

Was this page helpful?
0 / 5 - 0 ratings