-
-
Notifications
You must be signed in to change notification settings - Fork 821
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
Fix subschemas merge #4549
base: master
Are you sure you want to change the base?
Fix subschemas merge #4549
Conversation
Add failing test
|
@isaackhlam is attempting to deploy a commit to the The Guild Team on Vercel. A member of the Team first needs to authorize it. |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Definitely a bug, but we probably need to make sure to only send mutations for root fields? I have to look over more closely when I am able. |
@yaacovCR There are quite some discussions, and also comments suggesting nested mutation is not supported in graphql spec I would argue that correctness of execution should not be based on mutation being executed serially, any non-trivial application will run on more than a single threaded graphql server. In fact, even query is not strictly free of side-effect, consider it may have the ability to populate a cache at some level, making subsequent calls eventually consistent. {
user(id:"1") {
postToTimeline(commentContent:"Happy Birthday Mark!") { # This is a serial field
comment {
addReaction(reaction: LIKE) # This is another serial field, that adds a reaction into the comment
}
}
}
} On the other hand, it is important to correctly pass the operation type to stitched schema at any level, as there may be different requirements for running query and mutation, e.g. consistency, authentication, data source etc. Our use case uses nested mutation, being aware of the consequence that mutation and data query can run in parallel. mutation {
post(id:"1") { # Post
postComment(comment:"Hello world") { # Post!
comments { # [Comment!]!, correct usage, result includes new comment
id
}
}
allComments { # [Comment!]!, undefined behavior, can return data with / without new comment
id
}
}
} We implements guards to operations only meant for |
Ok, so not a bug? my mistake, we don't currently support nested mutations? Although we could consider a feature request, I am not sure how this would work in practice, as sometimes for subfields u may want to stitch to queries, sometimes to mutations, so u cannot always go by the overall original operation type. Maybe some additional config could support this? I have to look more closely still at your example. |
Sorry that the request in graphql/graphql-spec#252 might have mislead you, our case consist of only erither whole operation on I think mutation side-effect at root field is not required by graphql spec, as long as serial execution is not needed. I would like to highlight 2 comments in the spec request thread: graphql/graphql-spec#252 (comment)
graphql/graphql-spec#252 (comment)
Our bug reported here shows that |
First of all, thanks for contributing! We appreciate the effort to improve the library. There is also a lot of interesting ideas here with regard to order of execution, which is a topic that probably could be fleshed out in greater depth overall, so it's great to see how these ideas are being utilized in the wild, especially in the context of schema stitching. I'm just going to address a few things, some are in relation to comments you have made, and in the end with respect to the specifics of the PR.
Within a mutation operation, root fields are definitely executed serially. Sadly, although nested "mutations" can be utilized simply by nesting the Mutation type, multiple nested mutations no longer have that guarantee of serial execution. As long as you only nest a single mutation, you should be safe. This comment is in line with what you quote:
So I believe we are in agreement as to what the spec currently supports! On to your specific case:
The gateway is correctly forwarding the root fields as mutations to the subschema. I think we can agree on that. It is only that when type merging occurs, it is assumed that all merged types (including merged root types) are accessed via query root fields (accessors). Putting aside whether this is a "fix" or "feature implementation" (because who cares, thank you for pointing out this edge case!!!!), I just want to highlight that any implementation cannot simply infer the accessor's parent root type from the original operation, because that would break a mutation in which we are stitching in fields from a query. Your test cases include mutations where we are using mutation accessors in the presence of a query accessor, but not a case (the usual case, I believe!) where we actually want to start from a mutation and use a query accessor. So, I think we need to simply introduce another field on the accessor specification to indicate the operation, i.e. besides specifying the |
Hi @yaacovCR, thanks for your quick reply, surely schema stitching, particularly remote schema, is kind of new ideas and deserves some discussion. There are two cases below, imo in terms of correctness, Behavior B is implementation-wise more correct since stitched schema will reproduce the Behavior A is more DX friendly to new graphql stitching user, as said, maybe a lot of graphql user doesn't care about the Also I have not came up with a sensible scenario where we start with I would propose to add a boolean value like
Behavior A: Mutation at root then query stitched fields not available from mutation's return
I think you mean the case that Gateway: type Query {
queryFoo: Foo!
}
type Mutation {
mutateFoo: Foo!
}
type Foo {
id: ID!
fooField: String!
} Stitched schema: type Query {
queryFooFromStitch: Foo!
}
type Foo {
id: ID!
stitchedFooField: String!
} where a query like mutation {
mutateFoo {
id
fooField
stitchedFooField
}
} will break It is a valid concern since a lot of graphql users may have already organized their mutation following the pattern that mutations are only at root mutation type, i.e. Behavior B: Migrate from decomposing a single schema, which resolvers depends on
|
Question: Does your nested mutation resolver do something different when included within a query operation? Like throw an error? |
it does in our case to throw a "mutation in query error" that, we forbids resolver intended for mutation being run in query It is a way to require developers using client library to explicitly use the Edit: e.g. in apollo client and server, query is considered cachable on whole query / field level, on both client and server-side, I am not particularly favorable towards apollo, but anyway it seem quite a lot of developer uses it because of its features. |
4946aeb
to
4be9073
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine it would require custom merge resolvers that modify info only as necessary based on the directive.
d34fb65
to
b8bf584
Compare
🚨 IMPORTANT: Please do not create a Pull Request without creating an issue first.
Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.
Description
Bug fix for on graphql query is
mutation
, stitched schema executedquery
rather thanmutation
.On test v1, There is no
*User
in the fieldQuery
so it returnsnull
when we useMutation
to query to server.On test v2, We add
*User
to the fieldQuery
, We can see it return operation type to beQuery
even we query withMutation
.The bug is caused by hardcode
'query'
in stitched schema, changing toinfo.operation.operation
can fix the bug.Detain explanation in #4508
Add failing test for the bug
Fix the bug by changing
'query'
tooperation: info.operation.operation
Co-authored-by:
Type of change
Please delete options that are not relevant.
Screenshots/Sandbox (if appropriate/relevant):
https://www.graphql-tools.com/docs/schema-stitching/stitch-type-merging#merging-flow
If such field name is not defined in Query of stitched schema, the merged resolver fails silently returning null result.
How Has This Been Tested?
To reproduce
query in both test case
Result
Result
On both tests we expected result to be
Test Environment:
@graphql-tools/graphql-file-loader: ^7.3.14
@graphql-tools/load: ^7.5.13
@graphql-tools/schema: ^8.3.13
@graphql-tools/stitch: ^8.6.12
@graphql-yoga/node: ^2.8.0
@vendia/serverless-express: ^4.8.0
Checklist:
Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...