aws / aws-appsync-community

The AWS AppSync community
https://aws.amazon.com/appsync
Apache License 2.0
506 stars 32 forks source link

AppSync empty selectionSetList in the GraphQL Info Object #358

Open vasyl-nvt opened 4 months ago

vasyl-nvt commented 4 months ago

Hello,

It seems selectionSetList is always empty when running queries returning a union type.

Simplified schema example:

    type Post {
      id: ID!
      content: String!
    }

    type QueryError {
      message: String!
    }

    union QueryResult = Post | QueryError

    type Query {
      getPost(id: String!): QueryResult
    }

Query Example:

    query GetPostQuery {
       getPost(id: "post-guid") {
            ... on Post {
                id
                content
            }
            ... on QueryError {
                message
            }
       }
    }

Lambda function receives $event.info.selectionSetList = [] for such type of queries on AWS AppSync.

Is there anything one can do to make it work?

P.S: It works correctly when running locally using Amplify AppSyncSimulator.

osadi commented 3 months ago

I ran in to the same issue. And I don't know how many times I read the documentation, but the last time I read it I saw this: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html#aws-appsync-resolver-context-reference-info-js

selectionSetList exposes only fields that belong to the current type. If the current type is an interface or union, only selected fields that belong to the interface are exposed.

So it seems like it's by design and not much that can be done about it.. I have a similar setup to you, where they can't share fields in an interface so I'm kind of stuck.

Maybe something like this could work, and passing it the ctx.info.selectionSetGraphQL, which seems to be populated:

export const gqlToSelectionSetList = (str: string) => {
  const START_TOKEN = '{';
  const END_TOKEN = '}';
  const FRAGMENT_TOKEN = '.';
  const SPACE_TOKEN = ' ';
  const FRAGMENT_DECLARATION_TOKEN = 'on';

  const selectionSetList: string[] = [];
  const path: string[] = [];

  let curr: string = '';
  let prev: string = '';

  for (const c of str.replaceAll('\n', '')) {
    switch (c) {
      case FRAGMENT_TOKEN: {
        break;
      }
      case SPACE_TOKEN: {
        if (curr !== FRAGMENT_DECLARATION_TOKEN && curr !== '') {
          selectionSetList.push([...path, curr].join('/'));
          prev = curr;
        }
        curr = '';
        break;
      }
      case START_TOKEN: {
        if (prev) path.push(prev);
        break;
      }
      case END_TOKEN: {
        path.pop();
        break;
      }
      default: {
        curr = curr + c;
      }
    }
  }
  return selectionSetList;
};

I have no idea how robust this would be, or if this is a solution 😄

vasyl-nvt commented 3 months ago

@osadi Thanks for pointing out to the documentation. It seems to explain the behaviour. But to me it looks like selectionSetList is a result of parsing a string selectionSetGraphQL and should be populated accordingly. AppSync simulator does it.