rmosolgo / graphql-ruby

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

Field arguments inaccessible when schema is created from_definition #2530

Closed dmaze closed 4 years ago

dmaze commented 5 years ago

We have been generally writing GraphQL services by writing out the GraphQL schema IDL and then creating the graphql-ruby object model by calling GraphQL::Schema.from_definition on it. This requires passing in a hash of resolver functions, which get passed the current object, the field arguments, and the current context. This setup has broken for us, specifically in graphql-ruby 1.9.8.

A typical resolver map might look like this:

RESOLVERS = {
  'Query' => {
    'echo' => ->(_obj, args, _ctx) { args.in.s }
  }.freeze
}.freeze

Notice that in the resolver function, we are calling args.in to get a specific input argument, and args.in.s to get a specific field value from that. We'd tie this together with the schema with code like:

SDL = <<~GRAPHQL
  input In { s: String! }
  type Query {
    echo(in: In!): String!
  }
GRAPHQL
Schema = GraphQL::Schema.from_definition(SDL, default_resolve: RESOLVERS)

Test code to call this:

QUERY = '{ echo(in: { s: "foo" }) }'
result = Schema.execute(QUERY)
p result

https://gist.github.com/dmaze/c93ec7aa36ee0b17df2ded223e3b5ac8 contains a complete example built around these fragments.

When I run this against graphql-ruby 1.9.7 it works correctly and I get an output

#<GraphQL::Query::Result @query=... @to_h={"data"=>{"echo"=>"foo"}}>

When I run this against graphql-ruby 1.9.8 or later, I get a NoMethodError where args.in is not defined.

NoMethodError: undefined method `in' for #<#<Class:0x306cf3ea>:0x2beee7ff>
                       <main> at graphql-from-definition-args.rb:22
                         call at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema/build_from_definition/resolve_map.rb:57
            build_object_type at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema/build_from_definition.rb:185
                 build_fields at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema/build_from_definition.rb:325
                      resolve at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/field.rb:248
                         call at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:321
                  invoke_core at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema/middleware_chain.rb:49
                       invoke at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema/middleware_chain.rb:38
                resolve_field at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:129
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
                 call_tracers at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:76
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
                resolve_field at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:128
            resolve_selection at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:92
                         each at org/jruby/RubyHash.java:1417
            resolve_selection at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:85
       resolve_root_selection at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:56
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
                 call_tracers at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:76
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
       resolve_root_selection at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:49
                  begin_query at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/execute.rb:31
                  begin_query at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:112
             run_as_multiplex at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:84
                          map at org/jruby/RubyArray.java:2584
             run_as_multiplex at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:83
                  run_queries at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:62
       instrument_and_analyze at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:186
          apply_instrumenters at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:29
        each_query_call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:46
        each_query_call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:41
        each_query_call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:45
                   call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:72
        each_query_call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:44
          apply_instrumenters at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:27
                   call_hooks at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:72
          apply_instrumenters at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/instrumentation.rb:26
       instrument_and_analyze at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:174
                  run_queries at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:61
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
                 call_tracers at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:76
                        trace at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/tracing.rb:62
                  run_queries at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:59
                      run_all at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/execution/multiplex.rb:49
                    multiplex at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema.rb:392
  with_definition_error_check at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema.rb:1279
                    multiplex at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema.rb:391
                      execute at /Users/dmaze/.rvm/gems/jruby-9.2.8.0/gems/graphql-1.9.13/lib/graphql/schema.rb:368
                       <main> at graphql-from-definition-args.rb:36

As the backtrace hints, I'm currently using jruby-9.2.8.0 for this test.

rmosolgo commented 5 years ago

Hi, thanks for the very detailed report! I honestly don't remember if this was intentional or not 🙈 I will look back at the recent changes and see where it popped up!

rmosolgo commented 5 years ago

By the way, the work around is to access those arguments by key, instead, for example:

args[:in] # instead of args.in

It's probably worth it to make the migration. In GraphQL 1.10, those args objects will be plain Ruby hashes, at least by default.

rmosolgo commented 4 years ago

Sorry, I don't realistically have the bandwidth to look into this further. I hope you can make the kind of refactor I suggested above! It will also be future-compatible, so it will definitely pay off.