Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add field selection to GQL directive #207

Open
patrickwall57 opened this issue Nov 11, 2020 · 2 comments
Open

Add field selection to GQL directive #207

patrickwall57 opened this issue Nov 11, 2020 · 2 comments

Comments

@patrickwall57
Copy link

This a continuation from slack that will hopefully be easier to read. Just for consistency sake, I will define the client as the end user, stitch as stitch, and downstream as a backend service that will be the target of a rest/gql directive.

Imagine the following schema

    extend type Agreement @key(fields: "Id") {
        Id: String @external
        Claims(ClientChannelId: String!): [Claim] @gql(url: "https://{{ schemaConfig.customerClaims.targetUrl }}/graphql",
            fieldName: "claimsByMigrationStatus",
            operationType: Query,
            arguments: {
                migrationStatus: "{exports.MigrationStatus}",
                clientChannelId: "{args.ClientChannelId}"
            }
        )
    }

    type Claim @policy(namespace: "customer", name: "customerclaimsapiget", args: { userRoles: "{jwt.scope.split(\" \")}" }) {
        Id: string
        StartDate: String
        ClientChannelId: String
        ClientAccountId: String
        ServiceRequestId: String

        ContactPoint(ClientChannelId: String!): [ContactPoint] @gql(url: "https://{{ schemaConfig.customerClaims.targetUrl }}/graphql",
            fieldName: "getContactPoints",
            operationType: Query,
            arguments: {
                contactPointSearch: {
                    ClientChannelId: "{args.ClientChannelId}",
                    ClientAccountId: "{source.ClientAccountId}",
                    SearchParameters: {
                        ServiceRequestId: "{source.ServiceRequestId}"
                    },
                    FilterParameters: {
                        ReferenceType: "SRVRQST",
                        ReferenceIdentifier: "{source.ServiceRequestId}"
                    }
                }
            }
        )
    }
    type ContactPoint {
        Id: String
        Type: String
    }

In this case, we will populate some claims objects from a GQL downstream, and then we will make an additional call downstream to populate the ContactPoint type. The ContactPoint query requires the ClientChannelId, ClientAccountId, and ServiceRequestId from the Claim type. The following query will work as expected

      Claims(ClientChannelId: "1E5CA959308202F08CB4005056A72010") {
        Id
        ServiceRequestId
        ClientAccountId
        ContactPoint(ClientChannelId: "1E5CA959308202F08CB4005056A72010") {
          Type
        }
      }

However, this query does not work and returns an error that the inputs to the Claims gql directive are null

      Claims(ClientChannelId: "1E5CA959308202F08CB4005056A72010") {
        Id
        ContactPoint(ClientChannelId: "1E5CA959308202F08CB4005056A72010") {
          Type
        }
      }

This puts a burden on our consumers to know which fields are required in the query in order to make downstream fields resolve correctly. As our schema grows this is becoming complicated for our users.

The way I understand the GQL directive, the selection of fields in the clients query will be passed through to the downstream gql server. In the first example,

client queries for Claim.ServiceRequestId and Claim.ClientAccountId and Claim.Id. Stitch receives the request and queries the downstream for Claim.ServiceRequestId, Claim.ClientAccountId, and Claim.Id. At this point, all three fields are available in stitch's source object and thusly available to child resolvers/directives

In the second example, since the client does not request the ServiceRequestId and ClientAccountId fields, they arent queried from the downstream gql server and thus arent available in stitch's source object. Since they arent in the source object, the ContactPoint resolver fails.

What i am wondering is this - Is there a way that I can hardcode some selection of fields in the GQL directive such that the fields I've hardcoded are merged with the fields from the client query. This would let me ensure that I always populate the fields required by any downstream resolver without the client having to understand that logic. To extend the example above would look something like this.

    extend type Agreement @key(fields: "Id") {
        Id: String @external
        Claims(ClientChannelId: String!): [Claim] @gql(url: "https://{{ schemaConfig.customerClaims.targetUrl }}/graphql",
            fieldName: "claimsByMigrationStatus",
            operationType: Query,
            arguments: {
                migrationStatus: "{exports.MigrationStatus}",
                clientChannelId: "{args.ClientChannelId}"
            },
            query: {
                  ServiceRequestId,
                  ClientAccountId
            }
        )
    }

    type Claim @policy(namespace: "customer", name: "customerclaimsapiget", args: { userRoles: "{jwt.scope.split(\" \")}" }) {
        Id: string
        StartDate: String
        ClientChannelId: String
        ClientAccountId: String
        ServiceRequestId: String

        ContactPoint(ClientChannelId: String!): [ContactPoint] @gql(url: "https://{{ schemaConfig.customerClaims.targetUrl }}/graphql",
            fieldName: "getContactPoints",
            operationType: Query,
            arguments: {
                contactPointSearch: {
                    ClientChannelId: "{args.ClientChannelId}",
                    ClientAccountId: "{source.ClientAccountId}",
                    SearchParameters: {
                        ServiceRequestId: "{source.ServiceRequestId}"
                    },
                    FilterParameters: {
                        ReferenceType: "SRVRQST",
                        ReferenceIdentifier: "{source.ServiceRequestId}"
                    }
                }
            }
        )
    }
    type ContactPoint {
        Id: String
        Type: String
    }

In this proposed example, the ClientAccountId and ServiceRequestId are always queried - regardless of whether or not the client queries them. Because they are queried from the downstream, they are availble in the source, and the ContactPoint resolver will work for both of the client queries i detailed above.

Hopefully this clears up what I was trying to describe.

@AvivRubys
Copy link
Contributor

Thanks, this definitely helps understanding the issue.
We use apollo federation to merge the schemas, but don't use the federation runtime.
This is a feature the federation runtime natively handles through the @requires directive, so I think a good direction to solve this would be adding runtime support for @requires

@patrickwall57
Copy link
Author

@AvivRubys - that would be great - my question is if you guys have resources / bandwidth to pick this up and if you do - do you have an estimate on when it could be done? I dont mean to be pushy, but depending on whether or not we have this will shape how i move forward with an issue we are trying to resolve.

If I can add the fields i need through requires, I wont have to work with my clients to make updates to their queries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants