rmosolgo / graphql-ruby

Ruby implementation of GraphQL
http://graphql-ruby.org
MIT License
5.38k stars 1.39k forks source link

No way to specify a directive on a field definition #2122

Closed dwayne closed 3 years ago

dwayne commented 5 years ago

Given the following directive:

module Directive
  class Permission < GraphQL::Schema::Directive
    description 'Directs the executor to include this field only if the user has the given permission.'

    locations(
      GraphQL::Schema::Directive::FIELD_DEFINITION
    )

    argument :value, String, required: true,
      description: 'The required permission.'

    def self.include?(_object, arguments, context)
      permission = arguments[:value]
      if context[:user]
        context[:user].has_permissions?(permission)
      else
        true
      end
    end
  end
end

There doesn't seem to be a documented way to add the directive to a field definition. Is it currently possible?

rmosolgo commented 5 years ago

It sounds like you're asking about using directives in the SDL, for example:

type MyObject {
  someField @permission(value: "admin")
}

Is that right?

There's no special handling for directives like that right now. If you parse a schema from SDL, you can access the underlying AST node as .ast_node.

cpinsach commented 5 years ago

@dwayne you may need to add directive(Directive::Permission) on your schema definition. This might help, but a quick .md about custom directives would be helpful as well.

dwayne commented 5 years ago

@rmosolgo Yes, that's right. I want to be able to specify directives on fields in my type definitions. I'm trying to enforce permissions via directives as suggested here and here.

@mpinsach Thanks for the suggestion. I did all that. But my issue is that there is no way in the DSL to specify directives on the field definitions, in particular the fields of the root query.

jturkel commented 5 years ago

I was struggling with a similar problem i.e. how to use directives to factor out cross cutting behavior in my schema that was defined with the class based Ruby API rather than the GraphQL SDL. Then I stumbled across field extensions which worked well for my use case. Perhaps the intent is that directives should be used to allow query authors to modify execution behavior or factor out common execution handling with SDL defined schemas and field extensions/resolvers/standard Ruby constructs like modules should be used to factor out commonality in Ruby defined schemas?

Also since your example is security related, you might want to checkout the authorization framework if you haven't already done so.

dwayne commented 5 years ago

@jturkel I decided to use field metadata and write custom instrumentation.

For e.g.

class PermissionWhitelister
  def instrument(type, field)
    if field.metadata[:permissions]
      permissions = Array(field.metadata[:permissions])

      old_resolve_proc = field.resolve_proc
      new_resolve_proc = ->(object, arguments, context) {
        if context[:authorizer].has_permissions?(*permissions)
          old_resolve_proc.call(object, arguments, context)
        else
          raise GraphQL::ExecutionError, "You don't have the required permissions"
        end
      }

      field.redefine do
        resolve(new_resolve_proc)
      end
    else
      field
    end
  end
end
jturkel commented 5 years ago

It looks like field instrumentation is being deprecated in 1.10 (see #2174) so I think you'll eventually need to use a different approach.

dwayne commented 5 years ago

It looks like field instrumentation is being deprecated in 1.10 (see #2174) so I think you'll eventually need to use a different approach.

Thanks for the heads up.

glappen commented 5 years ago

So is it not possible to attach directives to field definitions in our schemes? I made a custom directive and attached it do a field in a query using standard @ syntax but it would be nicer to attach it to my schema for my use case (declaring that some fields might not exist on the underlying objective due to u dynamic data model).

rmosolgo commented 3 years ago

I recently released 1.12.0 which includes a "schema directives" system, please give it a try and open a new issue if you run into any trouble with it!

https://graphql-ruby.org/type_definitions/directives.html#schema-directives