Code Monkey home page Code Monkey logo

graphql-ruby's Introduction

graphql graphql-ruby

CI Suite Gem Version

A Ruby implementation of GraphQL.

Installation

Install from RubyGems by adding it to your Gemfile, then bundling.

# Gemfile
gem 'graphql'
$ bundle install

Getting Started

$ rails generate graphql:install

After this, you may need to run bundle install again, as by default graphiql-rails is added on installation.

Or, see "Getting Started".

Upgrade

I also sell GraphQL::Pro which provides several features on top of the GraphQL runtime, including:

Besides that, Pro customers get email support and an opportunity to support graphql-ruby's development!

Goals

  • Implement the GraphQL spec & support a Relay front end
  • Provide idiomatic, plain-Ruby API with similarities to reference implementation where possible
  • Support Ruby on Rails and Relay

Getting Involved

  • Say hi & ask questions in the #graphql-ruby channel on Discord.
  • Report bugs by posting a description, full stack trace, and all relevant code in a GitHub issue.
  • Start hacking with the Development guide.

graphql-ruby's People

Contributors

byroot avatar camiloforero avatar charlieschwabacher avatar choznerol avatar chrisbutcher avatar cjoudrey avatar connorshea avatar cpunion avatar daemonsy avatar danielvdao avatar dmitrytsepelev avatar dylanahsmith avatar eapache avatar esjee avatar gfx avatar gjtorikian avatar grcooper avatar hibariya avatar ianks avatar josh avatar jturkel avatar modosc avatar netshade avatar newx avatar rmosolgo avatar robertwsaunders avatar swalkinshaw avatar theorygeek avatar willianvdv avatar xuorig avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-ruby's Issues

Couldn't resolve field 'edges' to GraphQL::Relay::RelationConnection

Hello @rmosolgo

Getting following error when fetching next set of records using first: 10 parameter in Relay.

Error when fetching next posts (pagination)

 RuntimeError (Couldn't resolve field 'edges' to GraphQL::Relay::RelationConnection '#<GraphQL::Relay::RelationConnection:0x007ff75a0ff150>' (resulted in undefined method `engine' for #<Arel::Table:0x007ff759587a20>)):
11:16:11 web.1    |   app/controllers/graphql_controller.rb:8:in `create'
11:16:11 web.1    |   lib/graphql_reloader.rb:10:in `call'

This works in previous versions of the Gem (0.10.8), but not in the recent version.

Gems used

Latest version of graphql and graphql-relay
Rails version 5 beta

Make sure you can override `resolve_type`

Make it configurable like resolve

In the mean time, you can override it on objects themselves:

MyUnionType = UnionType.define # ... 
def MyUnionType.resolve_type(object) 
  # ... 
end

Testing with rspec

Hi
This is a big component in a new app I'm working on. How do you write tests with graphql-ruby? I haven't found any examples. I'm interested in testing queries, fragments and user authentification
Thank you

Parse error when query has a not-null variable

The following query from executing_queries.md doesn't parse:

GraphQL::PARSER.parse('query getPost($postId: !Int){ post(id: $postId) { title } }')
# => Parslet::ParseFailed: Extra input after last repetition at line 1 char 1.

If you remove the '!' in front of Int it works fine.
Since you plan to replace Parslet, maybe this isn't worth fixing but I thought I should mention it. I can just allow null for now.

Default values for variables are ignored

Example:

require 'graphql'
require 'json'

QueryRoot = GraphQL::ObjectType.define do
  name "Query"

  field :double, types.String do
    argument :x, types.Int
    resolve ->(obj, args, ctx) {
      "#{args['x'].inspect} ** 2"
    }
  end
end

Schema = GraphQL::Schema.new(query: QueryRoot)
query_string = <<GRAPHQL
query Q($x: Int = 3) {
  double(x: $x)
}
GRAPHQL
variables = {}
result = Schema.execute(query_string, variables: variables, debug: true)
puts JSON.pretty_generate(result)

outputs

/Users/dylansmith/src/graphql-ruby/lib/graphql/base_type.rb:77:in `coerce_input!': Couldn't coerce nil to Int (GraphQL::ExecutionError)
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:28:in `reduce_value'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:8:in `block in initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `each'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `reduce'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/field_resolution.rb:14:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/field_resolution.rb:14:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:92:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:92:in `block in resolve_field'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/directive_chain.rb:30:in `call'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/directive_chain.rb:30:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:91:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:91:in `resolve_field'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:34:in `block in result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `each'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `reduce'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/operation_resolution.rb:17:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/base_execution.rb:16:in `execute'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/executor.rb:50:in `execute'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/executor.rb:27:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query.rb:43:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/schema.rb:41:in `execute'
    from tmp/example.rb:22:in `<main>'

because it tries to coerce nil when the variable isn't provided, instead of replacing it with the default value.

Before commit fa7a2db, which added input coercion, it the output was

{
  "data": {
    "double": "nil ** 2"
  }
}

Field as Interface throwing error

I have an interface "ItemInterface" with two fields "id" and "position", Cover and Source implement this interface.
In an another type I have setup as field :item, ItemInterface. Whenever I query the object, I get the following error
{"errors"=>[{"message"=>"No such type Cover, so it can't be a fragment condition", "locations"=>[{"line"=>1, "column"=>57}]}]}

I don't get the above error if I set the field to a concrete type.
here's the part for the query { items { position ... on Cover {credit} } }
Am I doing something wrong?

Trouble parsing queries with empty strings

Looks like this was tackled back in #23, but querying using empty strings isn't completely working for me. When I try to query with:

mutation ClientMutation {
  company {
    update_user(id: "#{@user_1.id}", name: "Harriet", job_title: "") {
      id,
      name
    }
  }
}

I get:

NoMethodError: undefined method `parts' for #<Hash:0x0000000299b860>
    /home/chris/.rvm/gems/ruby-2.2.3/gems/graphql-0.10.1/lib/graphql/query.rb:47:in `initialize'
    /home/chris/.rvm/gems/ruby-2.2.3/gems/graphql-0.10.1/lib/graphql/schema.rb:40:in `new'
    /home/chris/.rvm/gems/ruby-2.2.3/gems/graphql-0.10.1/lib/graphql/schema.rb:40:in `execute'

It looks like while the parser usually returns a GraphQL::Language::Nodes::Document object, on the above query it actually returns a hash:

{:document_parts=>
  [{:operation_type=>"mutation"@6,
    :name=>"ClientMutation"@15,
    :variables=>[],
    :directives=>[],
    :selections=>
     [{:alias=>nil,
       :field_name=>"company"@40,
       :field_arguments=>[],
       :directives=>[],
       :selections=>
        {:optional_selections=>
          [{:alias=>nil,
            :field_name=>"update_user"@60,
            :field_arguments=>
             {:optional_field_arguments=>
               [#<GraphQL::Language::Nodes::Argument:0x00000001d551c8 @col=23, @line=3, @name="id", @value="1a0dde0c-8566-4edf-ba52-88a16620403a">,
                #<GraphQL::Language::Nodes::Argument:0x00000001c88740 @col=67, @line=3, @name="name", @value="Harriet">,
                {:field_argument_name=>"job_title"@133, :field_argument_value=>{:string=>[]}}]},
            :directives=>[],
            :selections=>
             [#<GraphQL::Language::Nodes::Field:0x000000016aa8f0 @alias=nil, @arguments=[], @col=13, @directives=[], @line=4, @name="id", @selections=[]>,
              #<GraphQL::Language::Nodes::Field:0x000000011f1750 @alias=nil, @arguments=[], @col=13, @directives=[], @line=5, @name="name", @selections=[]>]}]}}]}]}

I haven't dug deep enough to figure out why, though.

Validation breaking with variables sent by Relay

I'm not sure if this is an issue with this gem or graphql-relay-ruby, but when querying a connection field with an EnumType argument, graphql-ruby breaks without an error message, and raises the following:

undefined method 'type' for nil:NilClass

This error is being raised from /lib/graphql/static_validation/rules/variable_usages_are_allowed.rb:36:in validate_usage

This is because the caller of this method in the same file, validate, is setting var_defn_ast to nil, presumably because declared_variables[node.value.name] doesn't actually return anything (it's empty).

And when inspecting node here, it can be seen that it doesn’t even have a variables key:

#<GraphQL::Language::Nodes::Argument:0x007fb2a75f3870 @name="status", @value=#<GraphQL::Language::Nodes::VariableIdentifier:0x007fb2a34624b0 @name="status_0", @line=23, @col=44>, @line=23, @col=36>

Below are the breaking query and variable params:

query Landing {
    viewer{
        ..._0spmx34
      }
    }
    fragment _18n1pkt on Comic {
      title,
      slug,
      author {
        name,
        username,
        id
      },
      coverPage {
        aspectRatio,
        _imageqpcky9:image(size:\"thumb\") {
          uri
        },
        id},
      id
    }
    fragment _0spmx34 on Viewer {
      _comics56iyr5:comics(first:5,status:$status_0) {
        edges {
          node {
            id,
            coverPage {
              aspectRatio,
              _imageqpcky9:image(size:\"thumb\") {
                uri
              },
              id
            },
            ..._18n1pkt
          },
          cursor
        },
        pageInfo {
          hasNextPage,
          hasPreviousPage
        }
      }
    }

{"status_0" => "PUBLISHED"}

And just in case I'm defining something wrong, here’s my ViewerType:

ViewerType = GraphQL::ObjectType.define do

  name "Viewer"
  description "This is a workaround type for Relay's inability to handle root fields with more than one argument."

  connection :comics, ComicType.connection_type do

    argument :status, ComicStatusEnum, "The status of the requested comics"

    resolve -> (obj, args, ctx) {
      if args["status"]
        Comic.where(status: args['status'])
      else
        Comic.all
      end
    }
  end

end

Let me know if this is something that was never going to work in the first place 😅

Question: Does it support Polymorphic types?

Hey @rmosolgo,

I saw that you added Enum, Union and other types into the library. Can it be used for polymorphic associations? For example: A Post model and Comment model both can be voted using belongs_to :votable polymorphic association.

In normal situation, we can use Rails helpers to cleanly address this, but how can this be achieved with Relay mutations. In mutations, we need to return the fields (Post or Comment) connection when a mutation occurs. Currently, I have to create two mutations to achieve this: PostVoteMutation and CommentVoteMutation to return type: return_field :post, PostType and return_field :comment, CommentType

Is it possible to use the new types you introduced that will help in reducing code duplication?

Relay Helpers

It would be great to get some helpers for generating Relay types. Not sure if these should go here, or if we should break out another lib though.

I've got a simple Connection field helper for enumerables. We could probably generate an entire connection given an ActiveRecord::Relation (maybe for graphql-rails?)

Another one would be a Mutation field helper that automatically generates input/payload types. I've got something like this at the moment:

:change_profile_picture => MutationType.create do |m|
  m.accepts({
    :uri => !types.String
  })

  m.returns({
    :viewer => !ViewerType
  })

  m.resolve -> (object, args, ctx) { ... }
end

Missing coercion for literal argument values

My comment #46 (comment) showed an issue that hasn't been resolved, despite the issue being closed, so I thought I would open a new issue with an appropriate name.

Oh, it actually looks like values are also not coerced. E.g.

require 'graphql'
require 'json'

QueryRoot = GraphQL::ObjectType.define do
  name "Query"

  field :add, types.String do
    argument :a, types.Int
    argument :b, types.Int
    resolve ->(obj, args, ctx) {
      "#{args['a'].inspect} + #{args['b'].inspect}"
    }
  end
end

Schema = GraphQL::Schema.new(query: QueryRoot)
result = Schema.execute('{ add(a: 1.2, b: 2.3) }')
puts JSON.pretty_generate(result)

outputs

{
  "data": {
    "add": "1.2 + 2.3"
  }
}

because the static validator coerces the inputs, but the result isn't used, so the original float values get passed in as integer arguments.

Arguments can't be accessed with symbols

Not a bug, just a little annoying.

Arguments in resolve have to be accessed using a string (despite being defined as symbols). It might be nice to use something like HashWithIndifferentAccess from Rails, and automatically stringify symbols, just for the developers sake.

Needs a cool logo.

This project’s great, and the beginnings of documentation you have (and advice you've been dispensing on Slack) has been super helpful. I'd like to help out in my own small way and suggest a logo for this project, riffing off of Facebook's own GraphQL logo:

graph-ql-01

I've tried to use a more Ruby-like colour here, but I'd be happy with the original magenta, too.

Query::Arguments#to_h may return nested Arguments objects

I have a mutation that accepts an input_field a, which is an input object type with a field b.
In the mutation resolver, the inputs argument is something like this:

#<GraphQL::Query::Arguments:0x007f993d572aa0
 @hash={"a"=>#<GraphQL::Query::Arguments:0x007f993d572960 @hash={"b"=>5}, @values={"b"=>5}>},
 @values={"a"=>#<GraphQL::Query::Arguments:0x007f993d572960 @hash={"b"=>5}, @values={"b"=>5}>}>

calling to_h on it merely returns @hash, so I need to call to_h at every depth.

When calling Arguments.new on a nested hash, it recursively creates Arguments objects when setting @value, but the original is retained in @hash.
However when handling a real request, even the @hash has nested Arguments objects.
Is there some built-in way to unpack the Arguments recursively?

resolve with a Hash

Is there any way to resolve a field with a ordinary Ruby Hash? At the moment I need to convert the response of a particular API response into a list of OpenStructs, so that graphql can use public_send on it.

resolve ->(obj, args, ctx) {
  HTTParty.post('http://example.com')
    .parsed_response
    .fetch('some_key')
    .map { |data| OpenStruct.new(data) } # this step feels unnecessary and could be a potential performance problem
}

Variables aren't coerced.

Here is an example script that shows the problem:

require 'graphql'
require 'json'

QueryRoot = GraphQL::ObjectType.define do
  name "Query"

  field :foo, types.String do
    argument :input, types.Int
    resolve -> (obj, args, ctx) {
      args['input'].inspect
    }
  end
end

Schema = GraphQL::Schema.new(query: QueryRoot)

variables = { "input" => "not a int" }
result = Schema.execute(<<GRAPHQL, variables: variables)
query Q($input: Int) {
  foo(input: $input)
}
GRAPHQL

puts JSON.pretty_generate(result)

outputs

{
  "data": {
    "foo": "\"not a int\""
  }
}

despite the fact that a string variable value was accepted in an Int typed argument.

There is a check to make sure the declared variables type matches the argument type, but no coercion of the variable values themselves.

Cursors

Where do cursors fit in with GraphQL?

It seems like they are being used https://github.com/rmosolgo/graphql-ruby/blob/master/spec/support/nodes.rb#L115 and elsewhere to define the key that should be used in the JSON response, but everything that I've seen (like https://speakerdeck.com/relayjs/data-fetching-for-react-applications?slide=36), a "cursor" is just a field like any other, and keys are always just the name of the field requested (see https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html).

Nil Fields

If a field is nil, graphql-ruby still tries to instantiate a node and read properties off a nil target. It seems like it should just return a nil object, though that might have to wait for the official spec.

Field#resolve evaluates the @resolve_proc by Proc#call

in lib/graphql/field.rb#L60

  def resolve(object, arguments, context)
    @resolve_proc.call(object, arguments, context)
  end

Field instance evaluates the @resolve_proc by Proc#call. This makes the execution context an instance of GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig(where I defined the lambda). In this case, if I want to include helper methods that would be called by the @resolve_proc, I have to add them into DefinitionConfig. This seems odd to me.

It might be better if the @resolve_proc is called by #instance_exec(*args, &block), which makes the execution context to be the Field instance i defined. Thus I can include the helper methods into GraphQL::Field

Parse error using field with argument

In my schema I have a couple of fields that are computed rather than being attributes on the object in question so I use the block style + resolve to define them.

This works fine for one field that has no arguments e.g. the query:
{ some_model(id: 1) { id, some_computed_field } }

It causes a parse error (ParseError: Extra input after last repetition) if the field requires arguments
{ some_model(id: 1) { id, greeting(name: 'John') } }

Which is defined like this:

field :greeting do
  type !types.String
  argument :name, !types.String
  resolve -> (obj, args, ctx) do
    "Hello #{ name }"
  end
end     

Consider separating out GraphQL parsing

Facebook's implementation actually compiles GraphQL to an AST on the client side. This seems to be a prerequisite for Relay to work (maybe @leebyron or @schrockn can comment on this).

I propose that graphql-ruby starts moving in that direction. Instead of parsing GraphQL, the interface would accept and process a JSON AST. On the client side, you could use something like https://github.com/ooflorent/graphql-parser (or the Facebook reference implementation when it's released).

This would free graphql-ruby from worrying about parsing the query and handling variables. Everything would just come in the JSON AST. You could also fall back to processing a full query (for non-JS clients) using something like therubyracer.

I'm happy to work on this, but I'd like to hear your thoughts.

Outstanding questions about GraphQL

  1. Mutations. I heard that mutations are just "root calls with side effects". That makes sense. How do you provide inputs to that root call? For example, if you're creating a new account and you need to send over the person's data, how would you pass that? (I vaguely remember a special <input> object with keys and values. Was that plain JSON? Do other calls take JSON inputs?)

  2. Aliasing edges. I remember aliasing fields with field as other_name. I've implemented that. Can you also alias connections? If so, what's an example of that?

  3. Type system. Seems like the type system + required documentation brought a LOT of stability and discoverability to a GraphQL api. I'm ready to try implementing that, but I want to make sure I understand the system. Here's what I remember:

    • You can inspect an object's type with __type__, for example node(4) { __type__ { name } }
    • __type__ exposes name, description and ... what else? I imagine different objects expose different things.
    • You can query types directly with type(User)

    My remaining questions are:

    • What fields can you get a from a node's __type__?
    • What fields can you get from a connection's __type__?
    • Do fields expose a __type__? (eg node(4) { name { __type__} } or something?)
    • Can you inspect calls that are defined on connections or edges? Do calls expose their arguments & argument types?
    • Can you list root calls? Can you inspect individual root calls?

Fragment merging excluding fields

Similar to #55 I reported a little while ago. This time I am issuing the following query:

query Query {
  viewer {
    id
    payment(id: "f7781868-841c-4a32-94e9-8921a73635f7") {
      id
      ...__PaymentFragment1
      ...__PaymentFragment2
    }
  }
}

fragment __PaymentFragment1 on Payment {
  payee {
    name
    currency
  }
}

fragment __PaymentFragment2 on Payment {
  payee {
    name
  }
}

Whatever is in __PaymentFragment1 is completely ignored and so currency is never returned. Moving currency into __PaymentFragment2 will cause it to be returned in the response.

The AST on the context object is not flattened

As discussed on Slack the AST on the context object is not flattened, but should be.

Here is an example that shows the problem:

require 'graphql'

UserType = GraphQL::ObjectType.define do
  name 'User'
  description 'A user'

  field :id, !types.ID
  field :name, !types.String
end

PostType = GraphQL::ObjectType.define do
  name 'Post'
  description 'A post'

  field :id, !types.ID
  field :name, !types.String
  field :author, UserType
end

QueryType = GraphQL::ObjectType.define do
  field :posts do
    type types[PostType]
    resolve -> (_, args, context) do
      if context.ast_node.selections[1].selections[0].is_a?(GraphQL::Language::Nodes::FragmentSpread)
        raise "the AST is not flat"
      end
    end
  end
end

MySchema = GraphQL::Schema.new(query: QueryType)

query_string = "{
  posts {
    id
    author { ...userFields }
  }
}

fragment userFields on User {
  id
}
"

puts MySchema.execute(query_string, debug: true)

Regression: InputObjectType can't be specified with variables

commit fa7a2db introduced a regression where coerce gets called on ObjectInputType which doesn't implement the method

Example:

require 'graphql'
require 'json'

AddInput = GraphQL::InputObjectType.define do
  name "AddInput"
  input_field :a, types.Int
  input_field :b, types.Int
end

QueryRoot = GraphQL::ObjectType.define do
  name "Query"

  field :add, types.String do
    argument :input, AddInput
    resolve ->(obj, args, ctx) {
      input = args['input']
      "#{input['a'].inspect} + #{input['b'].inspect}"
    }
  end
end

Schema = GraphQL::Schema.new(query: QueryRoot)
query_string = <<GRAPHQL
query Q($input: AddInput) {
  add(input: $input)
}
GRAPHQL
variables = { "input" => { "a" => 1.2, "b" => 2.3 } }
result = Schema.execute(query_string, variables: variables, debug: true)
puts JSON.pretty_generate(result)

outputs

/Users/dylansmith/src/graphql-ruby/lib/graphql/base_type.rb:74:in `coerce_input!': undefined method `coerce_input' for AddInput:GraphQL::InputObjectType (NoMethodError)
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:28:in `reduce_value'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:8:in `block in initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `each'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `reduce'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/arguments.rb:6:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/field_resolution.rb:14:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/field_resolution.rb:14:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:92:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:92:in `block in resolve_field'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/directive_chain.rb:30:in `call'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/directive_chain.rb:30:in `initialize'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:91:in `new'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:91:in `resolve_field'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:34:in `block in result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `each'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `reduce'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/selection_resolution.rb:33:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/serial_execution/operation_resolution.rb:17:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/base_execution.rb:16:in `execute'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/executor.rb:50:in `execute'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query/executor.rb:27:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/query.rb:43:in `result'
    from /Users/dylansmith/src/graphql-ruby/lib/graphql/schema.rb:41:in `execute'
    from tmp/example.rb:29:in `<main>'

previously it would output

{
  "data": {
    "add": "1.2 + 2.3"
  }
}

Input fields to mutations aren't validated.

I have a mutation that looks like (simplified):

LaunchCourseMutation = GraphQL::Relay::Mutation.define do
  name "LaunchCourse"

  input_field :course_id, !UUIDType
  input_field :launched, !types.Boolean

  return_field :user_course, UserContentType

  resolve -> (inputs, ctx) {
    user_course = UserCourse.find_or_new(course_id: inputs['course_id'], user: User.current)
    user_course.launched_at = (Time.now if inputs['launched'.freeze])
    user_course.save
    {user_course: user_course}
  }
end

If the client leaves out the 'launched' param, though, the resolve function is still called without a 'launched' key in the inputs, when the gem should probably be returning an error before getting that far.

Whitespace parsing issues

I noticed some minor parsing issues with whitespace… not important now since so far Relay is generating accepted queries, but will become a bigger problem when GraphiQL lands.

Parses fine:
test(input: {variable: test})

Fails:
test(input: { variable: test })

(This is coming from memory so this might not be exact 😄)

input_field with a description causes error

I tried both of these (in a Mutation.define):

input_field :code, types.String do
  description "item code"
end

and

input_field :code, types.String, "item code"

Both of them cause this error when I do an IntrospectionQuery.

NoMethodError (undefined method `desc=' for #<GraphQL::Argument:0x007fba1e85a
860>)

If I remove the description, the error goes away. I'm using graphql 0.10.3 and graphql-relay 0.4.5.

Pre-loading/Eager loading nested associations

Hello @rmosolgo

Thank you for the Gem :)

Just been trying out GraphQL with rails and wondering, if I can eager load nested associations in parent type. For ex: If I have a model Post, which has many comments and then comment belongs to a user.

If I try to fetch this query:

query_string = "query getPostById { post(id: 1) {id, body, comments{body, user{id, email}}}}"
PostType = GraphQL::ObjectType.define do
  name "Post"
  description "A post entry"

  interfaces [NodeIdentification.interface]

  field :id, field: GraphQL::Relay::GlobalIdField.new('Post')
  field :title, types.String, "The title of this post"
  field :body, types.String,  "The body of this post"
  field :comments, -> { types[CommentType] }, "All comments of this post"
  field :user, UserType, "User associated with this post"

  field :comments_count do
    type types.Int
    description "Get comments count for this post"
    resolve -> (post, arguments, context) {
      post.comments.count
    }
  end

end

The above query runs 1 additional query per comment loading comment user. Normally, we would do like: post.comments.includes(:user), which would basically run two queries and get all comments plus its users.

Is I am missing anything?

Empty arrays not allowed in query

An empty array:

query test1 {
  testMe(array: [])
}

Causes this:

NoMethodError (undefined method `parts' for #<Hash:0x007ffa32b67f40>)

Making field property accessible after definition

When defining a field, you can specify a property name, which gets called on the backing object during resolve:

field :createdBy, types.String, property: :created_by

However, it seems like the property isn't accessible on the GraphQL::Field object; it's wrapped inside the resolver lambda in GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig:

property && field.resolve = -> (t,a,c) { t.public_send(property)}

Is there interest in storing this to an instance variable and exposing a getter?

My use case is to access the mappings in a mutation which needs to do a reverse mapping, to set attributes on an ActiveRecord model. E.g. the mutation receives a createdBy argument and has to call record.created_by = input_value. If there's a better way to do this, let me know.

Make a nice API for composition

What if you have a bunch of types with the same fields? I've been passing the objects into functions and adding fields to them, but it would be good to have a proper API for that. One idea I have is a "mixin", like:

HasBirthday = GraphQL::Mixin.define do 
  field(:birthday, DateType)
  field(:age_in_years, types.Int)
  field(:can_vote, types.Boolean) 
end

Person = GraphQL::ObjectType.define do 
  mixin(HasBirthday)
end

Cat = GraphQL::ObjectType.define do 
  mixin(HasBirthday)
end

Decorating types

Is there a good (read: D.R.Y.) way to Decorate objects?

Right now, we have some (ugly) code looking like this:

UserType = ::GraphQL::ObjectType.define do
  name "User"
  description "I am user"

  ...
  field :profile_picture_url do
    type !types.String
    argument :size, !types.Int, default_value: 50
    resolve -> (obj, args, ctx) {
      UserDecorator.new(obj).profile_picture_url(size: args[:size])
    }
  end
  ...
end

So I'm thinking something like coerce, but for object types:

UserType = ::GraphQL::ObjectType.define do
  name "User"
  description "I am user"
  decorate -> (obj) { UserDecorator.new(obj) }

  ...
  field :profile_picture_url do
    type !types.String
    argument :size, !types.Int, default_value: 50
    resolve -> (obj, args, ctx) {
      obj.profile_picture_url(size: args[:size])
    }
  end
  ...
end

I've naively tried to do this using coerce, but it doesn't seem to work for object types.

fragments shallowly merging causing dropped fields

I noticed an issue when using two fragments containing the same key, w/ others nested below. It should be deep merging, but instead the last fragment replaces any earlier ones. It ends up being an issue because Relay generates a lot of repetitive fragments.

this query:

{
  viewer {
    ...fragmentWithTwoFields
    ...fragmentWithOneField
  }
}

fragment fragmentWithTwoFields on User {
  todos {
    totalCount,
    completedCount,
  }
}

fragment fragmentWithOneField on User {
  todos {
    totalCount,
  }
}

produces:

{
  "data": {
    "viewer": {
      "todos": {
        "totalCount": 2
      }
    }
  }
}

when it should also include completedCount:

{
  "data": {
    "viewer": {
      "todos": {
        "totalCount": 2,
        "completedCount": 1
      }
    }
  }
}

Going to take a stab at fixing this tonight.

Docs: for array fields, explain why "!" is needed twice

It's not clear what the difference is between these two type definitions:

type !types[types.String]  # no "!" for wrapped type

and

type !types[!types.String]  # wrapped type has "!" also

The tests seem to only use the 2nd form, but the first seems to work. Is there a difference?

Parse error for empty input objects

mutation {
  createItem(input: 
    {
      clientMutationId: "112233",
      nested: {},  # <---- Empty input object can't be parsed
      title: "test mutation"
    }
  ) {
    returnField
  }
}

Error is: #<GraphQL::ParseError: Extra input after last repetition at line 1 char 1.
Please also verify that there can be spaces inside the braces.

Create GraphQL::ObjectType from AR Models?

Just a thought, but any desire to make it easy to create a GraphQL::ObjectType w/ associations automatically from an AR model?

I know people wouldn't automatically have a 1-1 between GraphQL types and AR models, but is something this project would aspire to do?

Library does not follow ruby naming conventions

The library is split up across several directories, but does not follow the naming conventions associated with that.

Example:
GraphQL::ObjectType should be at lib/graph_ql/object_type.rb, but it is at lib/graph_ql/types/object_type.rb, which would suggest GraphQL::Types::ObjectType.

Not following conventions makes it harder for developers and tooling to understand the code structure.

Mutation requests silently failing

Hello @rmosolgo

Getting following error from server when making a relay mutation (write) request:

Please note: All read requests works fine

Error (from relay transaction)

Console Logging

1. Variable input_0 of type CreateCommentInput! was provided invalid value "#<ActionController::Parameters:0x007fb5044ff950>"
   tion CreateComment($input_0:CreateCommentInput!){CreateComme

GraphQL query sent to server:

 | Started POST "/graphql" for ::1 at 2016-02-20 10:42:43 +0100
10:42:43 web.1    | Processing by GraphqlController#create as */*
10:42:43 web.1    |   Parameters: {"query"=>"mutation CreateComment($input_0:CreateCommentInput!){CreateComment(input:$input_0){clientMutationId,...F1,...F2}} fragment F0 on Post{id,comments_count} fragment F1 on CreateCommentPayload{post{id,...F0}} fragment F2 on CreateCommentPayload{commentEdge{cursor,__typename,node{id,body,created_at,user{name,id}}},post{id,...F0}}", "variables"=>{"input_0"=>{"post_id"=>"UG9zdC0xMDA=", "body"=>"ewr ewr werwerwerwerwerewr", "clientMutationId"=>"0"}}, "graphql"=>{"query"=>"mutation CreateComment($input_0:CreateCommentInput!){CreateComment(input:$input_0){clientMutationId,...F1,...F2}} fragment F0 on Post{id,comments_count} fragment F1 on CreateCommentPayload{post{id,...F0}} fragment F2 on CreateCommentPayload{commentEdge{cursor,__typename,node{id,body,created_at,user{name,id}}},post{id,...F0}}", "variables"=>{"input_0"=>{"post_id"=>"UG9zdC0xMDA=", "body"=>"ewr ewr werwerwerwerwerewr", "clientMutationId"=>"0"}}}}

Versions of gems used:

graphql 0.10.9
graphql-relay 0.6.2
Latest version of relay and babel-relay-plugin (0.7.1)
Rails version 5

Code

Blog App

Allow GraphQL::Query::Arguments to be converted into a regular Ruby hash

For better Rails integration it'd be nice to get a regular Hash from arguments. For example, ActiveRecord won't accept GraphQL args as an attributes hash. It'd be nice to be able to use other methods too like 'update'. My workaround for now is instance_variable_get(''@values").

What do you think about adding 'to_h' or 'unwrap'? I can make a PR.

Conversion of AST to value isn't done recursively

The problem is seen when an input object, enum or variable literal appears in a list or input object.

For example, a list of object literals will cause a list of GraphQL::Language::Nodes::InputObject objects to be passed into the resolve function rather than a list of GraphQL::Query::Arguments objects:

require 'graphql'
require 'json'

AttributeType = GraphQL::InputObjectType.define do
  name "Attribute"

  input_field :name, types.String
  input_field :value, types.String
end

QueryRoot = GraphQL::ObjectType.define do
  name "Query"

  field :test, types.String do
    argument :input, types[AttributeType]
    resolve ->(obj, args, ctx) {
      args['input'].inspect
    }
  end
end

Schema = GraphQL::Schema.new(query: QueryRoot)
query_string = <<GRAPHQL
query Q {
  test(input: [{ name: "a", value: "1" }, { name: "b", value: "2" }])
}
GRAPHQL
result = Schema.execute(query_string)
puts JSON.pretty_generate(result)

outputs

{
  "data": {
    "test": "[#<GraphQL::Language::Nodes::InputObject:0x007ff7dd738f20 @pairs=[#<GraphQL::Language::Nodes::Argument:0x007ff7dd729778 @name=\"name\", @value=\"a\", @line=2, @col=18>, #<GraphQL::Language::Nodes::Argument:0x007ff7dd7321c0 @name=\"value\", @value=\"1\", @line=2, @col=29>], @line=2, @col=18>, #<GraphQL::Language::Nodes::InputObject:0x007ff7dd7637c0 @pairs=[#<GraphQL::Language::Nodes::Argument:0x007ff7dd748a60 @name=\"name\", @value=\"b\", @line=2, @col=45>, #<GraphQL::Language::Nodes::Argument:0x007ff7dd75ab70 @name=\"value\", @value=\"2\", @line=2, @col=56>], @line=2, @col=45>]"
  }
}

Cannot specify input coercion separately from result coercion

The GraphQL specification distinguishes between input and result coercion, but this library doesn't.

Being able to define these separately would be very useful for custom scalar types, since the input coercion can deserialize the value into an appropriate object (e.g. Time) for use in the resolver, then result coercion can serialize the value so it can be represented in JSON. Otherwise, the resolver would always have to explicitly parse the input, so custom scalars would only be useful for validation.

Fragments querying same field with args not merging results

Querying with multiple fragments on the same field (same arguments, different attributes) results in only the last fragment's attributes being returned.

query Routes {
  viewer {
    id,
    ...__RelayQueryFragment3mkv3m6
  }
}

fragment __RelayQueryFragment4axva8b on Viewer {
  entity(profile_name:"foobar") {
    id,
    name,
    profile_name
  },
  id
}

fragment __RelayQueryFragment5o39n4b on Viewer {
  entity(profile_name:"foobar") {
    id,
    biography,
    default_tags
  },
  id
}

fragment __RelayQueryFragment3mkv3m6 on Viewer {
  id,
  ...__RelayQueryFragment4axva8b,
  ...__RelayQueryFragment5o39n4b
}

(The original query generated by Relay aliased both fields with the same key, but even without the aliases the problem occurs)

Response:

{
  "data": {
    "viewer": {
      "id": "...",
      "entity": {
        "id": "...",
        "biography": "",
        "default_tags": []
      }
    }
  }
}

I expect to also see name and profile_name fields in the response.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.