-
Notifications
You must be signed in to change notification settings - Fork 2k
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
didResolveField hooks are executed in a dangling promise and may receive the wrong result #4667
Labels
🔌 plugins
Relates to the request pipeline plugin API
Comments
sachindshinde
added a commit
that referenced
this issue
Oct 26, 2020
…es (#4654) We would like to inform users about what schema changes are safe, specifically: - Can an input object field be safely removed? - Can an input object field's default value be safely removed? - Can an input object field's type be safely changed to non-nullable? Will this require the addition of a default value? - Can an enum value be safely removed? This PR changes `ApolloServerPluginUsageReporting` to report stats on input object fields and enum values, which helps us answer these questions. Note that this only changes the plugin to look at request data. There are currently a few bugs in the `didResolveField` hook that make getting enum value stats from the response difficult, so I'm going to put off those changes until later (see #4667 for more info).
The best fix here would be to extend |
glasser
added a commit
that referenced
this issue
Mar 5, 2024
Paired with @bbeesley. Updates whenResultIsFinished to call callback also on Promise of Array of Promises. Fixes (partially) #5372 and #4667. The test case we're fixing is `'passes result of Promise of Array of Promises to the callback'`, the other tests test existing behaviour. The existing bug prevented us from dynamically setting cache hints in the reference resolver depending on the result of an asynchronous operation. We discovered the bug within an integration test. If you @glasser have an idea how to add an integration test and would like to add it, that would be amazing! --------- Co-authored-by: David Glasser <glasser@apollographql.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
While trying to add some code to the
didResolveField
handler ofApolloServerPluginUsageReporting
, I noticed some issues:didResolveField
is executed in a dangling promise created inwhenResultIsFinished()
, so there's no way for the plugin to wait ondidResolveField
handlers to finish executing.result
is an array of elements where at least one element is a promise, the code inwhenResultIsFinished()
will pass anundefined
result to the handler if at least one promise rejects. In reality, if the type inside the list is nullable (e.g.[String]
), then the response will contain a result list, but with nulls for the rejected promises (e.g.["foo", null]
). In my particular use case, I need the actual result.whenResultIsFinished()
is trying to handle list cases, but it doesn't do it fully, e.g. a promise that resolves to a list of promises, or nested list types (e.g.[[String]]
).In terms of what the solution would be, two thoughts come to mind:
fieldResolver()
insidewrapField()
directly to thedidResolveField
handler. The user has to manually unwrap the promise-or-value according to GraphQL spec (really just adapting the code incompleteValueCatchingError()
in graphql-js and other functions further down the call stack). But it gives the user the most flexibility, and it's more performant in the event that the user doesn't need to resolve the entirety of the result. The downside is that this would be a backwards-incompatible change, and that doing the unwrapping can be hairy.completeValueCatchingError()
and the other functions it ends up calling, and use that to compute the result up until subfields begin executing. Basically in the copy-pasted version ofcompleteObjectValue()
, you'd leave out the call tocollectAndExecuteSubfields()
and just return the source object. (You'd need to make some changes around error handling as well, since those functions normally just add them to the execution context.) Similar to the first case, the result would be conveyed via a promise-or-value passed to thedidResolveField
handler. This is probably closest to what a user imagines when they think of "result" in this context, requires the least work from their end, and can be backwards compatible (I say "can" because right nowdidResolveField
accepts a single error instead of an array, but that could be worked around e.g. with a new argument todidResolveField
). This has the downside in that it may perform a lot of unneeded work (e.g. serializing scalars), and duplicate a lot of data in memory (e.g. for lengthy arrays). In my case, I'm just looking to get a set of the enum values that are in the result, so I don't need to duplicate the entire result in-memory, and I don't care about non-enum results. Another downside is that this option assumes the user cares specifically about the result as it appears in the GraphQL response and not about the result as returned by the resolver, though this can be easily remedied through a new argument todidResolveField
e.g.rawResult
/resultBeforeCompletion
.The text was updated successfully, but these errors were encountered: