Gqlgen: Change value in Directive

Created on 3 Oct 2019  路  12Comments  路  Source: 99designs/gqlgen

Hi @vektah

I was able to achieve the constraint directive with the code below. However, I also want to sanitize my input values. How can I change the obj value that goes to the resolver inside the directive? For example, how can I change the input that comes into lowercase?

func ConstraintDirective(ctx context.Context, obj interface{}, next graphql.Resolver, pattern string, max int, min int, isLower bool) (res interface{}, err error) {
    v := obj.(string)
    r := regexp.MustCompile(pattern)

    if !r.MatchString(v) || len(v) < min || len(v) > max {
        return nil, gqlerror.Errorf("format")
    }

    return next(ctx)
}

Also is there a way to get the graphql input attribute name inside the directive?

_Originally posted by @ravilution in https://github.com/99designs/gqlgen/issues/868#issuecomment-533833995_

enhancement stale

All 12 comments

If you use the directive on the input the return value of next would be the input itself. There's probably some missing docs here

Hi @vektah, thank you for the response. I did not get you. Below is the sample query. The aim is to change username to lowercase before it reaches resolver. Also is there a way to GET the input variable name (in this case 'username') in the directive function?

type Mutation {
loginUser(
    username: String!
      @constraint(
        pattern: "^[a-z0-9]+[a-z0-9-]*[a-z0-9]+$"
        max: 20
        min: 3
        isLower: true
      )
    password: String!
  ): Boolean!
}

something along these lines should do it

func ConstraintDirective(ctx context.Context, obj interface{}, next graphql.Resolver, pattern string, max int, min int, isLower bool) (res interface{}, err error) {
        v = next(ctx).(string)
    r := regexp.MustCompile(pattern)

    if !r.MatchString(v) || len(v) < min || len(v) > max {
        return nil, gqlerror.Errorf("format")
    }

    return strings.ToLower(v)
}

next is the incoming value, could be from the input field itself, or another directive thats been applied, the return value is what you want to send to the resolver.

Hi @vektah, you are the best. I highly appreciate your timely response. It works. I now understand the role of next.

One last query. How can I get the attribute name in the directive function? I want the username attribute name. I want to know for which attribute name the directive function is getting executed. Any ideas?

mutation {
  done: loginUser(username: "Adam", password: "Test@123")
}

Sorry, I don't think that's currently supported. What's the use case?

Imagine the below mutation query. At the time of the request, imagine phonePassCode input was sent with the wrong pattern. The error message from the directive function must say "phonePassCode format error". For that message, I need to know inside the directive function that the error is for phonePassCode.

activateUser(
    phonePassCode: String!
      @constraint(pattern: "^[0-9]+$", max: 4, min: 4, isLower: false)
    emailPassCode: String!
      @constraint(pattern: "^[0-9]+$", max: 4, min: 4, isLower: false)
  )

I imagine path in the error response should automatically be set to the right thing for you, but I don't think it is right now.

The path value is activateUser :-(

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@vektah Any news on when path will be set correctly or if we have access to the attribute name? I am having the same issue.

directive @length(min: Int, max: Int) on INPUT_FIELD_DEFINITION

input NewMeetup @goModel(model: "github.com/penthious/go-gql-meetup/models.NewMeetup") {
    name: String! @length(min: 3)
    description: String!
}
type Mutation {
    createMeetup(input: NewMeetup!): Meetup!
}

The logic:

    c.Directives.Length = func(ctx context.Context, obj interface{}, next graphql.Resolver, min *int, max *int) (interface{}, error) {

        v, _ := next(ctx)
        if len(v.(string)) < *min {
            return nil, gqlerror.Errorf("format")
        }

        return v, nil
    }

The return value looks like

{
  "errors": [
    {
      "message": "format",
      "path": [
        "createMeetup"
      ]
    }
  ],
  "data": null
}

The path is pointing to the mutation that calls my input rather than the input_field that my directive is set on.

Got the same problem. I tried looking for a workaround using RequestContext and the gqlerror.ErrorPathf but couldn't find anything that worked consistently as I wasn't sure which arg was being validated.

@vektah I see you've started working on FieldContext https://github.com/99designs/gqlgen/blob/412a72fe26b093b08d27e90adf0390ad0ea0a7ea/graphql/context_field.go#L15, will this fix what's documented above or should we raise a separate issue?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

theoks picture theoks  路  3Comments

jszwedko picture jszwedko  路  3Comments

cemremengu picture cemremengu  路  3Comments

andrewmunro picture andrewmunro  路  4Comments

steebchen picture steebchen  路  3Comments