-
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
[BE-615] AER: use a static identifier for GraphQL validation failures #3465
[BE-615] AER: use a static identifier for GraphQL validation failures #3465
Conversation
This needs an upstream PR on |
packages/apollo-engine-reporting/src/__tests__/extension.test.ts
Outdated
Show resolved
Hide resolved
8b8814b
to
aa8d823
Compare
Addressed — please re-review when possible :)
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.
(can't respond to one thread, putting it here)
No more prettier in this project, but I fixed it manually.
prettier
is still installed at a consistent version in apollo-server; it just isn't automatically enforced via a linter. However, apollo-engine-reporting still is perfectly formatted with prettier, and I'd like to keep it this way. @abernix and I talked this week. I understand why he thinks Prettier specifically isn't a good fit for our open source projects but he agrees that that doesn't need to mean that we should regard our files as having no style rules at all, or that we shouldn't add in other style enforcement via eslint (just not the line length rules from prettier specifically). Let's keep this package prettier-clean, manually for now and maybe automatically later.
// and the operation name and signature will always be reported with a static | ||
// identifier. Whether the operation was a parse failure or a validation | ||
// failure will be embedded within the stats report key itself | ||
sendOperationDocumentsOnValidationFailure?: boolean; |
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.
Why not separate flags for validation and parsing? Or a name that is explicit? I really think it's misleading to use the word "validation" to mean "parse or validation". Words have meanings.
} else { | ||
const signature = await this.getTraceSignature({ | ||
queryHash, | ||
documentAST, |
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 don't get how this works: this variable is nullable but the parameter it's being passed to isn't?
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.
The null case is handled above in an else if
branch, so here we know that documentAST
is defined.
@@ -177,6 +193,7 @@ export class EngineReportingExtension<TContext = any> | |||
// isn't actually in the document. We want to know the name in that case | |||
// too, which is why it's important that we save the name now, and not just | |||
// rely on requestContext.operationName (which will be null in this case). | |||
this.gqlValidationSucceeded = true; |
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.
Similarly, at least have a comment that this would better belong in validationDidEnd. Though frankly you could also just change graphql-extensions to pass validationErrors to validationDidEnd. I made plenty of graphql-extensions changes as needed by this file.
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.
It seemed like making more extensions-specific changes would make this logic harder to port to the new plugin API in a way that was pretty certainly in alignment.
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.
That would actually align the stack API with the new plugin API, which already passes validation errors to its version of validationDidEnd.
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.
Also graphql-extensions
is a bit of a beast I just prefer not to touch 😅 Along with the fact that we already know there is overhead for each function call we're putting onto the stack, which is currently causing aer to spew tons of garbage, adding another function for validationDidEnd
is not something I really really want to do, but if you want to push for it, I shall comply :P
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.
The function already exists, it just doesn't receive the list of errors...
// Optional: when GraphQL parsing or validation against the GraphQL schema fails, these fields | ||
// can include reference to the operation being sent for users to dig into the set of operations | ||
// that are failing validation. | ||
string operationBodyOnValidationFailure = 27; |
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.
similarly, these shouldn't use "validation" to mean "parsing or validation". Split the fields or make a better name.
Maybe unexecutedOperationBody / unexecutedOperationName?
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 like it!
2d9e00f
to
b0f2429
Compare
sorry for the lag. will try to get to this early next week. looks like it needs a rebase. |
@@ -177,6 +193,7 @@ export class EngineReportingExtension<TContext = any> | |||
// isn't actually in the document. We want to know the name in that case | |||
// too, which is why it's important that we save the name now, and not just | |||
// rely on requestContext.operationName (which will be null in this case). | |||
this.gqlValidationSucceeded = true; |
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.
That would actually align the stack API with the new plugin API, which already passes validation errors to its version of validationDidEnd.
539d2da
to
815b562
Compare
@@ -65,11 +65,17 @@ test('trace construction', async () => { | |||
addTrace, |
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.
How about end-to-end tests! Search for eg sets the trace key to operationName when it is defined
… failure. Addresses feedback in below referenced [[Comment]]. If operation resolution (parsing and validating the document followed by selecting the correct operation) resulted in the population of the `operationName`, we'll use that. (For anonymous operations, `requestContext.operationName` is null, which we represent here as the empty string.) If the user explicitly specified an `operationName` in their request but operation resolution failed (due to parse or validation errors or because there is no operation with that name in the document), we still put _that_ user-supplied `operationName` in the trace. This allows the error to be better understood in Graph Manager. (We are considering changing the behavior of `operationName` in these three error cases; see [[#3465]] below for details.) [Comment]: #3998 (comment) [#3465]: #3465
8c3488a
to
d04a273
Compare
0afac33
to
786bdcb
Compare
@@ -163,6 +163,18 @@ export const plugin = <TContext>( | |||
} | |||
} | |||
|
|||
/** | |||
* This is set to true at the beginning of the pipeline. If we resole |
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.
minor spelling/capitalization/punctuation notes: "resolve", "GraphQL", "can start as true", "operation, the operation must"
Josh should update this commit message to be accurate :)
d66ef1e
to
483ce9a
Compare
Josh should update this commit message to be accurate :)
7fc3644
to
4f70449
Compare
4f70449
to
e5e8dbb
Compare
14e4218
to
fd9c1d5
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.
overall looking great
packages/apollo-server-core/src/plugin/usageReporting/__tests__/plugin.test.ts
Outdated
Show resolved
Hide resolved
packages/apollo-server-core/src/plugin/usageReporting/options.ts
Outdated
Show resolved
Hide resolved
packages/apollo-server-core/src/plugin/usageReporting/options.ts
Outdated
Show resolved
Hide resolved
</td> | ||
<td> | ||
|
||
Whether to include the entire document in the trace if the operation was a GraphQL parse or validation error (i.e. failed the GraphQL parse or validation phases). This will be included as a separate field on the trace and the operation name and signature will always be reported with a cosntant identifier. Whether the operation was a parse failure or a validation failure will be embedded within the stats report key itself. |
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.
cosntant -> constant
But also I think we can make this be a little more user-focused. Maybe something more along the lines of
Statistics about operations that your server cannot execute are not reported under each document separately to Apollo's servers, but are grouped together as "parse failure", "validation failure", or "unknown operation name". By default, the usage reporting plugin does not include the full operation document in reported traces, because it is challenging to strip potential private information (like string constants) from invalid operations. If you'd like the usage reporting plugin to include the full operation document so you can view it in Apollo Studio's trace view, set this to true.
The wording isn't ideal but I think this helps explain what's going on and why you might want to set it better?
7d322cf
to
d71abe9
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 have some changelog suggestions, and i do hope you remember to make the commit message up to date, but otherwise looks great!
…pii-in-invalid-graphql
for the PR description (final commit message when squashing) make sure you have double |
I've retargeted this to |
We've already done #3465, so we no longer need a single const to represent "either the actual resolved operation name that definitely points to a real parsed and validated operation, or else what the user wrote in the request". We can use the latter in the one case where we report an unexecuted operation name, and the former otherwise.
We've already done #3465, so we no longer need a single const to represent "either the actual resolved operation name that definitely points to a real parsed and validated operation, or else what the user wrote in the request". We can use the latter in the one case where we report an unexecuted operation name, and the former otherwise.
This PR changes the apollo usage reporting library to use static identifiers for operation documents that are not able to be executed.
When users of this module receive many un-executable operation documents, such as a non parse-able operation documents, invalid operation documents, or invalid operation names, every operation document is sent to Apollo Studio. This results in a cardinality explosion within Graph Manager. After a few thousand of these invalid operation names / documents are reported, the UI for the customer is borderline unusable due to the cardinality explosion & schema validation reaches a capacity of operations as well.
In general, we want to avoid storing & exposing personal information in Studio, and in current reporting agents, this is also problematic for operations that fail to execute. Because we currently report these operations with a signature matching the entire operation body, this is an easy trap for users to accidentally send user information through our system, when argument literals exist in the document.
The static identifiers are:
## GraphQLParseFailure
for documents that don't parse as valid graphql## GraphQLValidationFailure
for documents that aren't valid given the schema running on the server## GraphQLUnknownOperationName
for operation documents which don't have an operation name in the documentAdditionally, it allows users to optionally include the body of the operations that fail validation with
sendUnexecutableOperationDocuments
. This will send the operation body as part of the trace so they can be viewed alongside the trace.