-
Notifications
You must be signed in to change notification settings - Fork 14
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
feature: Add variable validation rule to query save #249
feature: Add variable validation rule to query save #249
Conversation
@@ -306,6 +307,15 @@ public function valid_or_throw( $post_content, $post_id ) { | |||
throw new RequestError( sprintf( __( 'This query has already been associated with another query "%s"', 'wp-graphql-smart-cache' ), $existing_post->post_title ) ); | |||
} | |||
|
|||
// During save/parse of query string, check for variables of the query string. | |||
// "To save this query, arguments should use variables instead of static values." | |||
$rules = DocumentValidator::defaultRules(); |
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.
This adds the other rules to the query string validation.
@@ -55,7 +55,7 @@ public function testQueriesAreDeletedByJob() { | |||
[ | |||
'post_type' => 'graphql_document', | |||
'post_date' => $date_string, | |||
'post_content' => sprintf( "query Saved_%d { typename }", $counter ), | |||
'post_content' => sprintf( "query Saved_%d { __typename }", $counter ), |
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.
Doing the rule validation, brought to attention some invalid test query strings. :)
// "query FooQuery { post( id: "1", idType: DATABASE_ID) { title } }" <-- this is the one we want to error on !!!!! | ||
// "query ($num: ID!) { post( id: "1", idType: DATABASE_ID) { title } }" | ||
// "query ($num: ID!) { post( id: $num, idType: DATABASE_ID) { title } }" | ||
// looking for [4] => argument: (id) ()\n [5] => variable: (num)\n |
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.
Oh, my notes are in here. Ha!
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.
We'd want to error on all 3 of these examples.
The problem we're trying to prevent, is folks doing string concatenation for their queries and instead push folks toward using variables.
All 3 of those queries could be concatenated like so:
example: query FooQuery { post( id: "1", idType: DATABASE_ID) { title } }
const id = 123;
const query = `query FooQuery { post( id: ${id}, idType: DATABASE_ID) { title } }`
example: query ($num: ID!) { post( id: "1", idType: DATABASE_ID) { title } }
(perhaps this was a mistake, but this looks like it might be invalid already as an argument is defined but not used)
const id = 123;
const idType: "DATABASE_ID";
const query = `query ($num: ID!) { post( id: ${id}, idType: ${idType}) { title } }`
example: query ($num: ID!) { post( id: $num, idType: DATABASE_ID) { title } }
const idType = "DATABASE_ID";
const query = `query ($num: ID!) { post( id: $num, idType: ${idType}) { title } }`
In each of these cases we have arguments passed in to fields that do not map to an argument defined for the query.
The expected "valid" definition of the query would be:
query ($id: ID! $idType: PostIdType ) {
post( id: $id idType: $idType ) {
title
}
}
Here, the field has 2 arguments, and both are defined in the operation.
If either of the arguments are not defined on the operation and used at the field level as an argument, then we might still be letting concatenated query strings persist many permutations of the same query, which is the situation we're trying to prevent.
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 believe this validates fully. See my comments re: how this still allows the problem we're trying to prevent.
// "query FooQuery { post( id: "1", idType: DATABASE_ID) { title } }" <-- this is the one we want to error on !!!!! | ||
// "query ($num: ID!) { post( id: "1", idType: DATABASE_ID) { title } }" | ||
// "query ($num: ID!) { post( id: $num, idType: DATABASE_ID) { title } }" | ||
// looking for [4] => argument: (id) ()\n [5] => variable: (num)\n |
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.
We'd want to error on all 3 of these examples.
The problem we're trying to prevent, is folks doing string concatenation for their queries and instead push folks toward using variables.
All 3 of those queries could be concatenated like so:
example: query FooQuery { post( id: "1", idType: DATABASE_ID) { title } }
const id = 123;
const query = `query FooQuery { post( id: ${id}, idType: DATABASE_ID) { title } }`
example: query ($num: ID!) { post( id: "1", idType: DATABASE_ID) { title } }
(perhaps this was a mistake, but this looks like it might be invalid already as an argument is defined but not used)
const id = 123;
const idType: "DATABASE_ID";
const query = `query ($num: ID!) { post( id: ${id}, idType: ${idType}) { title } }`
example: query ($num: ID!) { post( id: $num, idType: DATABASE_ID) { title } }
const idType = "DATABASE_ID";
const query = `query ($num: ID!) { post( id: $num, idType: ${idType}) { title } }`
In each of these cases we have arguments passed in to fields that do not map to an argument defined for the query.
The expected "valid" definition of the query would be:
query ($id: ID! $idType: PostIdType ) {
post( id: $id idType: $idType ) {
title
}
}
Here, the field has 2 arguments, and both are defined in the operation.
If either of the arguments are not defined on the operation and used at the field level as an argument, then we might still be letting concatenated query strings persist many permutations of the same query, which is the situation we're trying to prevent.
$I->seePostInDatabase( [ | ||
'post_type' => 'graphql_document', | ||
'post_status' => 'publish', | ||
'post_content' => "query (\$num: ID!) {\n post(id: \$num, idType: DATABASE_ID) {\n title\n }\n}\n", |
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.
This query should fail validation because the idType
could be concatenated still, leading to duplicate query documents being stored for different possible values.
i.e.
const idTye = 'DATABASE_ID';
const query = `query ($num: ID!) { post( id: $num, idType: ${idTye}) { title } }`
or:
const idTye = 'SLUG';
const query = `query ($num: ID!) { post( id: $num, idType: ${idTye}) { title } }`
By using string interpolation / concatenation, we'd be able to have multiple versions of the same query, which is the situation we're trying to prevent.
Ideally we'd be guiding the user to:
const query = `query ($num: ID! $idType: PostIdType) { post( id: $num, idType: $idType }) { title } }`
Where here, each argument on the field level corresponds with a variable defined at the operation level.
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.
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 tried another query, and was able to publish it without error.
I'm not super familiar with what's available in the ArgumentNode
, but I'm curious if we can do any other type of check other than the in_array() check here 🤔
I wonder if there's prior art in the JavaScript GraphQL Ecosystem for this type of validation rule too? Maybe we can borrow from them, if there is.
If this is merged as is, apps with existing APQ style persisted queries that break this validation rule will fail to save. Thus a change in behavior and an impact to caching performance. Consider making this validation on saving queries a) optional, opt-in, with a checkbox. b) only enforced in WP admin editor while saving a query. |
closing for now. We can reference this PR when work begins again on #224. |
When processing a persisted query document string for saving, run the validation rules on the string. Add a new rule to error for arguments that are present in operations but instead should be a variable instead. Return the graphql Error or display the error in the editor.
Github issue