-
Notifications
You must be signed in to change notification settings - Fork 493
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
Add Validating Directives #583
Add Validating Directives #583
Conversation
graphql_test.go
Outdated
Variables: map[string]interface{}{"episode": "NEWHOPE"}, | ||
ExpectedResult: "null", | ||
ExpectedErrors: []*gqlerrors.QueryError{ | ||
{Message: `rebel scum cannot request imperial units`, Locations: []gqlerrors.Location{{Line: 58, Column: 3}}, Path: []interface{}{"hero", "height"}}, |
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.
Maybe
{Message: `rebel scum cannot request imperial units`, Locations: []gqlerrors.Location{{Line: 58, Column: 3}}, Path: []interface{}{"hero", "height"}}, | |
{Message: `rebes cannot request imperial units`, Locations: []gqlerrors.Location{{Line: 58, Column: 3}}, Path: []interface{}{"hero", "height"}}, |
graphql_test.go
Outdated
Unit string | ||
}) | ||
if ok && v.Unit == "FOOT" { | ||
return fmt.Errorf("rebel scum cannot request imperial units") |
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.
same
@@ -47,26 +47,32 @@ type Field struct { | |||
HasContext bool | |||
HasError bool | |||
ArgsPacker *packer.StructPacker | |||
DirectiveVisitors []directives.ResolverInterceptor | |||
DirectiveVisitors *FieldDirectives |
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.
Technically speaking the type here FieldDirectives
is a bit misleading because these are being applied to FIELD_DEFINITION
. And I would like to add field directives too, which are applied on FIELD
and are packed at query exec time.
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.
Will have to think on the naming here. Intent is that they are the directives applied to the schema field. In the resolvable
package, the schema field is referred to as Field
// For now this is the only valid directive function | ||
if _, ok := v.(directives.ResolverInterceptor); !ok { | ||
switch v.(type) { | ||
default: |
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.
default last, please
internal/exec/validate.go
Outdated
errs[i] = &errors.QueryError{ | ||
Err: err, | ||
Message: err.Error(), | ||
Locations: []errors.Location{f.field.Loc}, // TODO: is this the correct location? |
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.
LGTM
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 should’ve added more context in the comment here: I’m unclear whether the location should be where the field is defined in the schema definition, or where in the requested query the field is
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 are validating the query.
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 is the location in the schema being applied here, rather than the query. I'm not sure that we have access to the other
d3994f5
to
f0be47b
Compare
I resolved conflicts in this PR. |
social.Schema, | ||
"role: Role!", | ||
`role: Role! @hasRole(role: "ADMIN")`, | ||
), |
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.
(nitpick) You could have used schema extension here : )
I'll add examples how to do that soon.
switch sel := sel.(type) { | ||
case *selected.SchemaField: | ||
field, ok := fieldByAlias[sel.Alias] | ||
if !ok { // validation already checked for conflict (TODO) |
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.
What does // validation already checked for conflict (TODO)
mean in this context?
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.
Copy-pasted from the execX equivalent, I believe would apply here as well, but not sure what the intended long-term resolution is
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 guess we are checking that the alias doesn't exist as a field. This is fine I guess. I'll add a unit test to confirm that. Not related to your PR, though.
This comment was marked as resolved.
This comment was marked as resolved.
After thinking a little bit more, directives should not be part of the resolver. Instead, they should indeed be passed as a schema option. The reason for that is the possibility to parse a schema without actually passing any resolver. This would allow us to inspect the schema and to call [Schema.AST] on it. In future we might have some directives on |
Adding support for directives which are evaluated prior to executing any resolvers. This allows validation to be performed on the request and prevent it from executing any significant work by rejecting the request early. The most obvious case for this is authorization: based on the requested fields, we can tell whether the request is valid given the current user, and reject the entire request. If that were applied at resolution time, the request would have partially resolved, only to return errors for the specific fields which are not authorized.
b6cd960
to
9ae7df1
Compare
r.Errs = errs | ||
out.Write([]byte("null")) | ||
return | ||
} |
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 can add a global short circuit here. If the schema doesn't have any validation directives, then this entire block can be skipped.
Adding support for directives which are evaluated prior to executing any resolvers. This allows validation to be performed on the request and prevent it from executing any significant work by rejecting the request early. The most obvious case for this is authorization: based on the requested fields, we can tell whether the request is valid given the current user, and reject the entire request. If that were applied at resolution time, the request would have partially resolved, only to return errors for the specific fields which are not authorized.
Adding support for directives which are evaluated prior to executing any resolvers. This allows validation to be performed on the request and prevent it from executing any significant work by rejecting the request early. The most obvious case for this is authorization: based on the requested fields, we can tell whether the request is valid given the current user, and reject the entire request. If that were applied at resolution time, the request would have partially resolved, only to return errors for the specific fields which are not authorized.
Adding support for directives which are evaluated prior to executing any resolvers. This allows validation to be performed on the request and prevent it from executing any significant work by rejecting the request early.
The most obvious case for this is authorization: based on the requested fields, we can tell whether the request is valid given the current user, and reject the entire request. If that were applied at resolution time, the request would have partially resolved, only to return errors for the specific fields which are not authorized.
fixes: #570