Skip to content
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 field info to directive context #1084

Open
seaskyways opened this issue Mar 3, 2020 · 9 comments
Open

Add field info to directive context #1084

seaskyways opened this issue Mar 3, 2020 · 9 comments
Labels

Comments

@seaskyways
Copy link

seaskyways commented Mar 3, 2020

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 vektah added the accepted label Mar 5, 2020
@swkumar
Copy link

swkumar commented Mar 9, 2020

@vektah , @seaskyways, We encountered this issue as well: #990 (comment)

Adding it here for completeness

@Gilwe
Copy link

Gilwe commented Apr 20, 2020

Hi @seaskyways @vektah,
Is there an ETA for this?

@logrusorgru
Copy link

Is there an ETA for this?

Seems, its abandoned.

@frederikhors
Copy link
Collaborator

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!

@muhammad-i2o
Copy link

muhammad-i2o commented Dec 10, 2020

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)
		}
	}
}

@appcypher
Copy link

appcypher commented Mar 10, 2021

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
}

@cyberhck
Copy link

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,

@appcypher
Copy link

The field name is useful for error handling.

@cyberhck
Copy link

ahh, I'm actually accepting a error message as second argument :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants