-
Notifications
You must be signed in to change notification settings - Fork 9
Description
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.