gqlgen
gqlgen copied to clipboard
Add field info to directive context
What happened?
I was writing a directive to validate fields. GQLgen did not give me the functionality I need.
I have the following directive that applies on INPUT_FIELD_DEFINITION
:
func (r resolver) directiveValidate(
ctx context.Context, obj interface{}, next graphql.Resolver, rules string,
) (res interface{}, err error) {
v := validator.New()
err = v.VarCtx(ctx, obj, rules)
if err != nil {
return
}
return next(ctx)
}
Unfortunately there is no way for me to check which field is this directive running on right now. obj
is pointing to the parent object instead of the field the directive is on.
What did you expect?
I expect to receive in the directive information regarding the field related perhaps in the function context.
What I need is the field's name as in the schema and the field's value either provided in ctx
or obj
.
Minimal graphql.schema and models to reproduce
directive @validate(rules: String!) on INPUT_FIELD_DEFINITION
input Login {
# unless I explicitly pass to the directive the field name, there is no way to know what field is being "validated" when the directive runs.
username: String! @validate(rules: "min=6")
}
versions
-
gqlgen version
? 0.11.1 -
go version
? 1.13.8 - dep or go modules? go modules
@vektah , @seaskyways, We encountered this issue as well: https://github.com/99designs/gqlgen/issues/990#issuecomment-585550923
Adding it here for completeness
Hi @seaskyways @vektah, Is there an ETA for this?
Is there an ETA for this?
Seems, its abandoned.
Is there an ETA for this?
Seems, its abandoned.
It's not. It's going a little slow lately, but they'll be back brighter than before!
Hi,
We have identified an approach that works for us at i2O with gqlgen
version 0.13.0
. It relies on a built-in directive which we noticed is added to all fields as part of the generated code (generated.go
).
The directive returns the value of the actual field which can then be used and modified accordingly.
func ToLowerDirective(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
// Run built-in gqlgen directive first
obj, err = next(ctx)
if err != nil {
return nil, err
}
pathContext := graphql.GetPathContext(ctx)
if pathContext != nil {
// Directive is being applied to an input field or argument
// obj here is the value of the input / argument after it has been resolved by the built-in gqlgen directive
fieldName := *pathContext.Field
switch value := obj.(type) {
case string:
return strings.ToLower(value), nil
default:
return nil, errors.Errorf("input field or argument \"%s\" must be a string", fieldName)
}
} else {
// Directive is being applied to a returned entity's field
// obj here is the value of the field after it has been resolved by the built-in gqlgen directive
fieldContext := graphql.GetFieldContext(ctx)
fieldName := fieldContext.Field.Name
switch value := obj.(type) {
case string:
return strings.ToLower(value), nil
default:
return nil, errors.Errorf("field \"%s\" must be a string", fieldName)
}
}
}
Here is how I solved it. Pretty similar to @muhammad-i2o's solution but it is all you need if your directive is applied on INPUT_FIELD_DEFINITION
.
config.Directives.Validate = func(ctx context.Context, obj interface{}, next graphql.Resolver, rules *string) (interface{}, error) {
val, err := next(ctx)
if err != nil {
return val, err
}
fmt.Println(">>> fieldname =", *graphql.GetPathContext(ctx).Field)
fmt.Println(">>> fieldvalue =", val)
fmt.Println(">>> rules =", *rules)
return val, nil
}
I just tried this and everything looks good, I can use validation on input fields, we don't really need field name, do we, we just need the rules and the actual value,
The field name is useful for error handling.
ahh, I'm actually accepting a error message as second argument :)