graphql 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9cb8700392b11bc4b4e577e38b424c13b3d33dd9
4
- data.tar.gz: 36f2b6d48b2d11084aeb4d0ea50c194e1f2a4b27
3
+ metadata.gz: 5651b5fa2ec7dde473a9747b9899bcb99a71b1d5
4
+ data.tar.gz: a3c045b8d41109f2867090f68c0868eb68671d55
5
5
  SHA512:
6
- metadata.gz: c8498188be3457f626d74653e48bee19558fc3030f383d8fe65cdf3cfa7f0cf9c8eeabafb1de634a48f9f991de77a0e2630056356de5854af78adbc03881cc14
7
- data.tar.gz: de3a1fcfdecefe84e9ed578c9840e861670df763dfb7bd96b11e97e7971c6cbd10c669674cc574904ae9fd0b0db29d70f3dac1bf2aa1f3e430320067dd88859a
6
+ metadata.gz: e4ca9364a51bc9d85927d3a00f9d06514a386a482078366167f647eb483455d0bb052041a921f0c107e84cb872a26343d26684c6c625a5fcc8ba19132191ffae
7
+ data.tar.gz: 6c60beeafb45f1246293c4aa559e04a0240ca1e2a9d82f3323f3a34320bd2d5703f3b38aa647ad21b57128a9121cb9ec2a1c213dd0d42400a1bce801a3da31f0
@@ -76,18 +76,14 @@ module GraphQL
76
76
 
77
77
  alias :inspect :to_s
78
78
 
79
- # Coerce `input_value` according to this type's `coerce` method.
80
- # Raise an error if the value becomes nil.
81
- # @param [Object] Incoming query value
82
- # @return [Object] Coerced value for query execution
83
- def coerce_input!(input_value)
84
- coerced_value = coerce_input(input_value)
85
-
86
- if coerced_value.nil?
87
- raise GraphQL::ExecutionError.new("Couldn't coerce #{input_value.inspect} to #{self.unwrap.name}")
88
- end
79
+ def valid_input?(value)
80
+ return true if value.nil?
81
+ valid_non_null_input?(value)
82
+ end
89
83
 
90
- coerced_value
84
+ def coerce_input(value)
85
+ return nil if value.nil?
86
+ coerce_non_null_input(value)
91
87
  end
92
88
  end
93
89
  end
@@ -41,6 +41,10 @@ class GraphQL::EnumType < GraphQL::BaseType
41
41
  GraphQL::TypeKinds::ENUM
42
42
  end
43
43
 
44
+ def valid_non_null_input?(value_name)
45
+ @values_by_name.key?(value_name)
46
+ end
47
+
44
48
  # Get the underlying value for this enum value
45
49
  #
46
50
  # @example get episode value from Enum
@@ -49,7 +53,7 @@ class GraphQL::EnumType < GraphQL::BaseType
49
53
  #
50
54
  # @param value_name [String] the string representation of this enum value
51
55
  # @return [Object] the underlying value for this enum value
52
- def coerce_input(value_name)
56
+ def coerce_non_null_input(value_name)
53
57
  @values_by_name.fetch(value_name).value
54
58
  end
55
59
 
@@ -19,12 +19,21 @@ class GraphQL::InputObjectType < GraphQL::BaseType
19
19
  GraphQL::TypeKinds::INPUT_OBJECT
20
20
  end
21
21
 
22
- def coerce_input(value)
22
+ def valid_non_null_input?(input)
23
+ return false unless input.is_a?(Hash) || input.is_a?(GraphQL::Query::Arguments)
24
+ return false unless input.all? { |name, value| input_fields[name] }
25
+ input_fields.all? { |name, field| field.type.valid_input?(input[name]) }
26
+ end
27
+
28
+ def coerce_non_null_input(value)
23
29
  input_values = {}
24
30
  input_fields.each do |input_key, input_field_defn|
25
- raw_value = value.fetch(input_key, input_field_defn.default_value)
26
- field_type = input_field_defn.type
27
- input_values[input_key] = field_type.coerce_input!(raw_value)
31
+ field_value = value[input_key]
32
+ field_value = input_field_defn.type.coerce_input(field_value)
33
+ if field_value.nil?
34
+ field_value = input_field_defn.default_value
35
+ end
36
+ input_values[input_key] = field_value unless field_value.nil?
28
37
  end
29
38
  GraphQL::Query::Arguments.new(input_values)
30
39
  end
@@ -17,8 +17,12 @@ class GraphQL::ListType < GraphQL::BaseType
17
17
  "[#{of_type.to_s}]"
18
18
  end
19
19
 
20
- def coerce_input(value)
21
- inner_type = of_type
22
- value.map { |item| inner_type.coerce_input!(item) }
20
+ def valid_non_null_input?(value)
21
+ return false unless value.is_a?(Array)
22
+ value.all?{ |item| of_type.valid_input?(item) }
23
+ end
24
+
25
+ def coerce_non_null_input(value)
26
+ value.map{ |item| of_type.coerce_input(item) }
23
27
  end
24
28
  end
@@ -13,6 +13,10 @@ class GraphQL::NonNullType < GraphQL::BaseType
13
13
  "Non-Null"
14
14
  end
15
15
 
16
+ def valid_input?(value)
17
+ !value.nil? && of_type.valid_input?(value)
18
+ end
19
+
16
20
  def coerce_input(value)
17
21
  of_type.coerce_input(value)
18
22
  end
data/lib/graphql/query.rb CHANGED
@@ -6,10 +6,17 @@ class GraphQL::Query
6
6
  end
7
7
  end
8
8
 
9
- class VariableMissingError < StandardError
10
- def initialize(name, type)
11
- msg = "Variable #{name} of type #{type} can't be null"
9
+ class VariableValidationError < GraphQL::ExecutionError
10
+ def initialize(variable_ast, type, reason)
11
+ msg = "Variable #{variable_ast.name} of type #{type} #{reason}"
12
12
  super(msg)
13
+ self.ast_node = variable_ast
14
+ end
15
+ end
16
+
17
+ class VariableMissingError < VariableValidationError
18
+ def initialize(variable_ast, type)
19
+ super(variable_ast, type, "can't be null")
13
20
  end
14
21
  end
15
22
 
@@ -102,7 +109,6 @@ require 'graphql/query/context'
102
109
  require 'graphql/query/directive_chain'
103
110
  require 'graphql/query/executor'
104
111
  require 'graphql/query/literal_input'
105
- require 'graphql/query/ruby_input'
106
112
  require 'graphql/query/serial_execution'
107
113
  require 'graphql/query/type_resolver'
108
114
  require 'graphql/query/variables'
@@ -8,7 +8,7 @@ module GraphQL
8
8
 
9
9
  def initialize(values)
10
10
  @values = values.inject({}) do |memo, (inner_key, inner_value)|
11
- memo[inner_key] = wrap_value(inner_value)
11
+ memo[inner_key.to_s] = wrap_value(inner_value)
12
12
  memo
13
13
  end
14
14
  end
@@ -19,7 +19,7 @@ module GraphQL
19
19
  @values[key.to_s]
20
20
  end
21
21
 
22
- def_delegators :@values_hash, :keys, :values, :each
22
+ def_delegators :@values, :keys, :values, :each
23
23
 
24
24
  private
25
25
 
@@ -13,7 +13,9 @@ module GraphQL
13
13
  # @return [Hash] A GraphQL response, with either a "data" key or an "errors" key
14
14
  def result
15
15
  execute
16
- rescue GraphQL::Query::OperationNameMissingError, GraphQL::Query::VariableMissingError => err
16
+ rescue GraphQL::ExecutionError => err
17
+ {"errors" => [err.to_h]}
18
+ rescue GraphQL::Query::OperationNameMissingError => err
17
19
  {"errors" => [{"message" => err.message}]}
18
20
  rescue StandardError => err
19
21
  query.debug && raise(err)
@@ -2,47 +2,16 @@ module GraphQL
2
2
  class Query
3
3
  # Turn query string values into something useful for query execution
4
4
  class LiteralInput
5
- attr_reader :variables, :value, :type
6
- def initialize(type, incoming_value, variables)
7
- @type = type
8
- @value = incoming_value
9
- @variables = variables
10
- end
11
-
12
- def graphql_value
13
- if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
14
- variables[value.name] # Already cleaned up with RubyInput
15
- elsif type.kind.input_object?
16
- input_values = {}
17
- inner_type = type.unwrap
18
- inner_type.input_fields.each do |arg_name, arg_defn|
19
- ast_arg = value.pairs.find { |ast_arg| ast_arg.name == arg_name }
20
- raw_value = resolve_argument_value(ast_arg, arg_defn, variables)
21
- reduced_value = coerce(arg_defn.type, raw_value, variables)
22
- input_values[arg_name] = reduced_value
23
- end
24
- input_values
25
- elsif type.kind.list?
26
- inner_type = type.of_type
27
- value.map { |item| coerce(inner_type, item, variables) }
28
- elsif type.kind.non_null?
29
- inner_type = type.of_type
30
- coerce(inner_type, value, variables)
31
- elsif type.kind.scalar?
32
- type.coerce_input!(value)
33
- elsif type.kind.enum?
34
- value_name = value.name # it's a Nodes::Enum
35
- type.coerce_input!(value_name)
5
+ def self.coerce(type, value, variables)
6
+ if value.is_a?(Language::Nodes::VariableIdentifier)
7
+ variables[value.name]
8
+ elsif value.nil?
9
+ nil
36
10
  else
37
- raise "Unknown input #{value} of type #{type}"
11
+ LiteralKindCoercers::STRATEGIES.fetch(type.kind).coerce(value, type, variables)
38
12
  end
39
13
  end
40
14
 
41
- def self.coerce(type, value, variables)
42
- input = self.new(type, value, variables)
43
- input.graphql_value
44
- end
45
-
46
15
  def self.from_arguments(ast_arguments, argument_defns, variables)
47
16
  values_hash = {}
48
17
  argument_defns.each do |arg_name, arg_defn|
@@ -59,31 +28,61 @@ module GraphQL
59
28
  GraphQL::Query::Arguments.new(values_hash)
60
29
  end
61
30
 
62
- private
31
+ module LiteralKindCoercers
32
+ module NonNullLiteral
33
+ def self.coerce(value, type, variables)
34
+ LiteralInput.coerce(type.of_type, value, variables)
35
+ end
36
+ end
63
37
 
64
- def coerce(*args)
65
- self.class.coerce(*args)
66
- end
38
+ module ListLiteral
39
+ def self.coerce(value, type, variables)
40
+ if value.is_a?(Array)
41
+ value.map{ |element_ast| LiteralInput.coerce(type.of_type, element_ast, variables) }
42
+ else
43
+ [LiteralInput.coerce(type.of_type, value, variables)]
44
+ end
45
+ end
46
+ end
67
47
 
68
- def resolve_argument_value(*args)
69
- self.class.resolve_argument_value(*args)
70
- end
48
+ module InputObjectLiteral
49
+ def self.coerce(value, type, variables)
50
+ hash = {}
51
+ value.pairs.each do |arg|
52
+ field_type = type.input_fields[arg.name].type
53
+ hash[arg.name] = LiteralInput.coerce(field_type, arg.value, variables)
54
+ end
55
+ type.input_fields.each do |arg_name, arg_defn|
56
+ if hash[arg_name].nil?
57
+ value = LiteralInput.coerce(arg_defn.type, arg_defn.default_value, variables)
58
+ hash[arg_name] = value unless value.nil?
59
+ end
60
+ end
61
+ Arguments.new(hash)
62
+ end
63
+ end
71
64
 
72
- # Prefer values in this order:
73
- # - Literal value from the query string
74
- # - Variable value from query varibles
75
- # - Default value from Argument definition
76
- def self.resolve_argument_value(ast_arg, arg_defn, variables)
77
- if !ast_arg.nil?
78
- raw_value = ast_arg.value
65
+ module EnumLiteral
66
+ def self.coerce(value, type, variables)
67
+ type.coerce_input(value.name)
68
+ end
79
69
  end
80
70
 
81
- if raw_value.nil?
82
- raw_value = arg_defn.default_value
71
+ module ScalarLiteral
72
+ def self.coerce(value, type, variables)
73
+ type.coerce_input(value)
74
+ end
83
75
  end
84
76
 
85
- raw_value
77
+ STRATEGIES = {
78
+ TypeKinds::NON_NULL => NonNullLiteral,
79
+ TypeKinds::LIST => ListLiteral,
80
+ TypeKinds::INPUT_OBJECT => InputObjectLiteral,
81
+ TypeKinds::ENUM => EnumLiteral,
82
+ TypeKinds::SCALAR => ScalarLiteral,
83
+ }
86
84
  end
85
+ private_constant :LiteralKindCoercers
87
86
  end
88
87
  end
89
88
  end
@@ -19,14 +19,10 @@ module GraphQL
19
19
  # Then, In a second pass, we resolve the flattened set of fields
20
20
  selections
21
21
  .reduce({}){|memo, ast_node|
22
- flatten_selection(ast_node).each do |name, selection|
23
- if memo.has_key? name
24
- memo[name] = merge_fields(memo[name], selection)
25
- else
26
- memo[name] = selection
27
- end
22
+ flattened_selections = flatten_selection(ast_node)
23
+ flattened_selections.each do |name, selection|
24
+ merge_into_result(memo, selection)
28
25
  end
29
-
30
26
  memo
31
27
  }
32
28
  .values
@@ -38,28 +34,49 @@ module GraphQL
38
34
  private
39
35
 
40
36
  def flatten_selection(ast_node)
41
- return {(ast_node.alias || ast_node.name) => ast_node} if ast_node.is_a?(GraphQL::Language::Nodes::Field)
37
+ strategy_method = STRATEGIES[ast_node.class]
38
+ send(strategy_method, ast_node)
39
+ end
40
+
41
+ STRATEGIES = {
42
+ GraphQL::Language::Nodes::Field => :flatten_field,
43
+ GraphQL::Language::Nodes::InlineFragment => :flatten_inline_fragment,
44
+ GraphQL::Language::Nodes::FragmentSpread => :flatten_fragment_spread,
45
+ }
42
46
 
43
- ast_fragment = get_fragment(ast_node)
44
- return {} unless fragment_type_can_apply?(ast_fragment)
47
+ def flatten_field(ast_node)
48
+ result_name = ast_node.alias || ast_node.name
49
+ return { result_name => ast_node }
50
+ end
45
51
 
52
+ def flatten_inline_fragment(ast_node)
46
53
  chain = GraphQL::Query::DirectiveChain.new(ast_node, query) {
47
- ast_fragment.selections.reduce({}) do |memo, selection|
48
- memo.merge(flatten_selection(selection))
49
- end
54
+ flatten_fragment(ast_node)
50
55
  }
56
+ chain.result
57
+ end
51
58
 
52
- chain.result || {}
59
+ def flatten_fragment_spread(ast_node)
60
+ ast_fragment_defn = query.fragments[ast_node.name]
61
+ chain = GraphQL::Query::DirectiveChain.new(ast_node, query) {
62
+ flatten_fragment(ast_fragment_defn)
63
+ }
64
+ chain.result
53
65
  end
54
66
 
55
- def get_fragment(ast_node)
56
- if ast_node.is_a? GraphQL::Language::Nodes::FragmentSpread
57
- query.fragments[ast_node.name]
58
- elsif ast_node.is_a? GraphQL::Language::Nodes::InlineFragment
59
- ast_node
60
- else
61
- raise 'Unrecognized fragment node'
67
+ def flatten_fragment(ast_fragment)
68
+ if !fragment_type_can_apply?(ast_fragment)
69
+ return {}
62
70
  end
71
+
72
+ flat_result = {}
73
+ ast_fragment.selections.each do |selection|
74
+ flat_selection = flatten_selection(selection)
75
+ flat_selection.each do |name, selection|
76
+ merge_into_result(flat_result, selection)
77
+ end
78
+ end
79
+ flat_result
63
80
  end
64
81
 
65
82
  def fragment_type_can_apply?(ast_fragment)
@@ -93,6 +110,20 @@ module GraphQL
93
110
  }
94
111
  chain.result
95
112
  end
113
+
114
+ def merge_into_result(memo, selection)
115
+ name = if selection.respond_to?(:alias)
116
+ selection.alias || selection.name
117
+ else
118
+ selection.name
119
+ end
120
+
121
+ if memo.has_key?(name)
122
+ memo[name] = merge_fields(memo[name], selection)
123
+ else
124
+ memo[name] = selection
125
+ end
126
+ end
96
127
  end
97
128
  end
98
129
  end
@@ -26,12 +26,18 @@ module GraphQL
26
26
  variable_name = ast_variable.name
27
27
  default_value = ast_variable.default_value
28
28
  provided_value = @provided_variables[variable_name]
29
- if !provided_value.nil?
30
- graphql_value = GraphQL::Query::RubyInput.coerce(variable_type, provided_value)
31
- elsif !default_value.nil?
32
- graphql_value = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, {})
33
- elsif variable_type.kind.non_null?
34
- raise GraphQL::Query::VariableMissingError.new(variable_name, variable_type)
29
+
30
+ unless variable_type.valid_input?(provided_value)
31
+ if provided_value.nil?
32
+ raise GraphQL::Query::VariableMissingError.new(ast_variable, variable_type)
33
+ else
34
+ raise GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, "was provided invalid value #{JSON.dump(provided_value)}")
35
+ end
36
+ end
37
+ if provided_value.nil?
38
+ GraphQL::Query::LiteralInput.coerce(variable_type, default_value, {})
39
+ else
40
+ variable_type.coerce_input(provided_value)
35
41
  end
36
42
  end
37
43
  end
@@ -2,7 +2,7 @@ module GraphQL
2
2
  # The parent type for scalars, eg {GraphQL::STRING_TYPE}, {GraphQL::INT_TYPE}
3
3
  #
4
4
  # @example defining a type for Time
5
- # TimeType = GraphQL::ObjectType.define do
5
+ # TimeType = GraphQL::ScalarType.define do
6
6
  # name "Time"
7
7
  # description "Time since epoch in seconds"
8
8
  #
@@ -19,7 +19,11 @@ module GraphQL
19
19
  self.coerce_result = proc
20
20
  end
21
21
 
22
- def coerce_input(value)
22
+ def valid_non_null_input?(value)
23
+ !coerce_non_null_input(value).nil?
24
+ end
25
+
26
+ def coerce_non_null_input(value)
23
27
  @coerce_input_proc.call(value)
24
28
  end
25
29
 
@@ -6,10 +6,10 @@ class GraphQL::StaticValidation::LiteralValidator
6
6
  elsif type.kind.list? && ast_value.is_a?(Array)
7
7
  item_type = type.of_type
8
8
  ast_value.all? { |val| validate(val, item_type) }
9
- elsif type.kind.scalar?
10
- !type.coerce_input(ast_value).nil?
9
+ elsif type.kind.scalar? && !ast_value.is_a?(GraphQL::Language::Nodes::AbstractNode) && !ast_value.is_a?(Array)
10
+ type.valid_input?(ast_value)
11
11
  elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
12
- !type.coerce_input(ast_value.name).nil?
12
+ type.valid_input?(ast_value.name)
13
13
  elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
14
14
  fields = type.input_fields
15
15
  ast_value.pairs.all? do |value|
@@ -18,7 +18,6 @@ class GraphQL::StaticValidation::LiteralValidator
18
18
  present_if_required && validate(value.value, field_type)
19
19
  end
20
20
  elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
21
- # Todo: somehow pass in the document's variable definitions and validate this
22
21
  true
23
22
  else
24
23
  false
@@ -17,7 +17,7 @@ class GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped
17
17
  else
18
18
  type = context.schema.type_from_ast(node.type)
19
19
  if !literal_validator.validate(value, type)
20
- context.errors << message("Default value for $#{node.name} doesn't match type #{node.type.name}", node)
20
+ context.errors << message("Default value for $#{node.name} doesn't match type #{type}", node)
21
21
  end
22
22
  end
23
23
  end
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.10.0"
2
+ VERSION = "0.10.1"
3
3
  end
data/readme.md CHANGED
@@ -109,6 +109,13 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
109
109
  - Big ideas:
110
110
  - Use [graphql-parser](https://github.com/shopify/graphql-parser) (Ruby bindings for [libgraphqlparser](https://github.com/graphql/libgraphqlparser)) instead of Parslet
111
111
  - Revamp the fixture Schema to be more useful (better names, more extensible)
112
+ - __Subscriptions__
113
+ - This is a good chance to make an `Operation` abstraction of which `query`, `mutation` and `subscription` are members
114
+ - For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
115
+ - Pre-process query strings?
116
+ - Remove `@skip`-ed things
117
+ - Inline any fragments
118
+ - Inline variables?
112
119
 
113
120
  ## Goals
114
121
 
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::ListType do
4
+ let(:float_list) { GraphQL::ListType.new(of_type: GraphQL::FLOAT_TYPE) }
5
+
6
+ it 'coerces elements in the list' do
7
+ assert_equal([1.0, 2.0, 3.0].inspect, float_list.coerce_input([1, 2, 3]).inspect)
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe GraphQL::Query::Arguments do
4
+ let(:arguments) { GraphQL::Query::Arguments.new({ a: 1, b: 2 }) }
5
+
6
+ it 'returns keys as strings' do
7
+ assert_equal(['a', 'b'], arguments.keys)
8
+ end
9
+
10
+ it 'delegates values to values hash' do
11
+ assert_equal([1, 2], arguments.values)
12
+ end
13
+
14
+ it 'delegates each to values hash' do
15
+ pairs = []
16
+ arguments.each do |key, value|
17
+ pairs << [key, value]
18
+ end
19
+ assert_equal([['a', 1], ['b', 2]], pairs)
20
+ end
21
+ end
@@ -189,5 +189,37 @@ describe GraphQL::Query::Executor do
189
189
  assert_equal(expected, result)
190
190
  end
191
191
  end
192
+
193
+ describe "for required input object fields" do
194
+ let(:variables) { {"input" => {} } }
195
+ let(:query_string) {%| mutation M($input: ReplaceValuesInput!) { replaceValues(input: $input) } |}
196
+ it "returns a variable validation error" do
197
+ expected = {
198
+ "errors"=>[
199
+ {
200
+ "message" => "Variable input of type ReplaceValuesInput! was provided invalid value {}",
201
+ "locations" => [{"line"=>1, "column"=>14}]
202
+ }
203
+ ]
204
+ }
205
+ assert_equal(expected, result)
206
+ end
207
+ end
208
+
209
+ describe "for input objects with unknown keys in value" do
210
+ let(:variables) { {"input" => [{ "foo" => "bar" }]} }
211
+ let(:query_string) {%| query Q($input: [DairyProductInput]) { searchDairy(product: $input) { __typename, ... on Cheese { id, source } } } |}
212
+ it "returns a variable validation error" do
213
+ expected = {
214
+ "errors"=>[
215
+ {
216
+ "message" => "Variable input of type [DairyProductInput] was provided invalid value [{\"foo\":\"bar\"}]",
217
+ "locations" => [{"line"=>1, "column"=>11}]
218
+ }
219
+ ]
220
+ }
221
+ assert_equal(expected, result)
222
+ end
223
+ end
192
224
  end
193
225
  end
@@ -103,6 +103,7 @@ describe GraphQL::Query do
103
103
  describe "merging fragments with different keys" do
104
104
  let(:query_string) { %|
105
105
  query getCheeseFieldsThroughDairy {
106
+ ... cheeseFrag3
106
107
  dairy {
107
108
  ...flavorFragment
108
109
  ...fatContentFragment
@@ -124,6 +125,21 @@ describe GraphQL::Query do
124
125
  fatContent
125
126
  }
126
127
  }
128
+
129
+ fragment cheeseFrag1 on Query {
130
+ cheese(id: 1) {
131
+ id
132
+ }
133
+ }
134
+ fragment cheeseFrag2 on Query {
135
+ cheese(id: 1) {
136
+ flavor
137
+ }
138
+ }
139
+ fragment cheeseFrag3 on Query {
140
+ ... cheeseFrag2
141
+ ... cheeseFrag1
142
+ }
127
143
  |}
128
144
 
129
145
  it "should include keys from each fragment" do
@@ -139,7 +155,11 @@ describe GraphQL::Query do
139
155
  "fatContent" => 0.04,
140
156
  }
141
157
  ],
142
- }
158
+ },
159
+ "cheese" => {
160
+ "id" => 1,
161
+ "flavor" => "Brie"
162
+ },
143
163
  }}
144
164
  assert_equal(expected, result)
145
165
  end
@@ -224,7 +244,7 @@ describe GraphQL::Query do
224
244
  let(:query_variables) { {"cheeseId" => "2"} }
225
245
 
226
246
  it "raises an error" do
227
- assert(result["errors"][0]["message"].include?(%{Couldn't coerce "2" to Int}))
247
+ assert_equal(result["errors"][0]["message"], %{Variable cheeseId of type Int! was provided invalid value "2"})
228
248
  end
229
249
  end
230
250
 
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::ScalarType do
4
+ let(:scalar) {
5
+ GraphQL::ScalarType.define do
6
+ name "BigInt"
7
+ coerce_input ->(value) { Integer(value) }
8
+ coerce_result ->(value) { value.to_s }
9
+ end
10
+ }
11
+ let(:bignum) { 2 ** 128 }
12
+
13
+ it 'coerces nil into nil' do
14
+ assert_equal(nil, scalar.coerce_input(nil))
15
+ end
16
+
17
+ it 'coerces input into objects' do
18
+ assert_equal(bignum, scalar.coerce_input(bignum.to_s))
19
+ end
20
+
21
+ it 'coerces result value for serialization' do
22
+ assert_equal(bignum.to_s, scalar.coerce_result(bignum))
23
+ end
24
+ end
@@ -18,7 +18,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
18
18
  let(:errors) { validator.validate(document) }
19
19
 
20
20
  it 'finds undefined arguments to fields and directives' do
21
- assert_equal(3, errors.length)
21
+ assert_equal(4, errors.length)
22
22
 
23
23
  query_root_error = {
24
24
  "message"=>"Argument id on Field 'cheese' has an invalid value",
@@ -26,6 +26,12 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
26
26
  }
27
27
  assert_includes(errors, query_root_error)
28
28
 
29
+ directive_error = {
30
+ "message"=>"Argument if on Directive 'skip' has an invalid value",
31
+ "locations"=>[{"line"=>4, "column"=>31}]
32
+ }
33
+ assert_includes(errors, directive_error)
34
+
29
35
  input_object_error = {
30
36
  "message"=>"Argument product on Field 'searchDairy' has an invalid value",
31
37
  "locations"=>[{"line"=>6, "column"=>7}]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-17 00:00:00.000000000 Z
11
+ date: 2015-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -214,7 +214,6 @@ files:
214
214
  - lib/graphql/query/directive_chain.rb
215
215
  - lib/graphql/query/executor.rb
216
216
  - lib/graphql/query/literal_input.rb
217
- - lib/graphql/query/ruby_input.rb
218
217
  - lib/graphql/query/serial_execution.rb
219
218
  - lib/graphql/query/serial_execution/field_resolution.rb
220
219
  - lib/graphql/query/serial_execution/operation_resolution.rb
@@ -277,12 +276,15 @@ files:
277
276
  - spec/graphql/language/parser_spec.rb
278
277
  - spec/graphql/language/transform_spec.rb
279
278
  - spec/graphql/language/visitor_spec.rb
279
+ - spec/graphql/list_type_spec.rb
280
280
  - spec/graphql/object_type_spec.rb
281
+ - spec/graphql/query/arguments_spec.rb
281
282
  - spec/graphql/query/base_execution/value_resolution_spec.rb
282
283
  - spec/graphql/query/context_spec.rb
283
284
  - spec/graphql/query/executor_spec.rb
284
285
  - spec/graphql/query/type_resolver_spec.rb
285
286
  - spec/graphql/query_spec.rb
287
+ - spec/graphql/scalar_type_spec.rb
286
288
  - spec/graphql/schema/field_validator_spec.rb
287
289
  - spec/graphql/schema/middleware_chain_spec.rb
288
290
  - spec/graphql/schema/printer_spec.rb
@@ -355,12 +357,15 @@ test_files:
355
357
  - spec/graphql/language/parser_spec.rb
356
358
  - spec/graphql/language/transform_spec.rb
357
359
  - spec/graphql/language/visitor_spec.rb
360
+ - spec/graphql/list_type_spec.rb
358
361
  - spec/graphql/object_type_spec.rb
362
+ - spec/graphql/query/arguments_spec.rb
359
363
  - spec/graphql/query/base_execution/value_resolution_spec.rb
360
364
  - spec/graphql/query/context_spec.rb
361
365
  - spec/graphql/query/executor_spec.rb
362
366
  - spec/graphql/query/type_resolver_spec.rb
363
367
  - spec/graphql/query_spec.rb
368
+ - spec/graphql/scalar_type_spec.rb
364
369
  - spec/graphql/schema/field_validator_spec.rb
365
370
  - spec/graphql/schema/middleware_chain_spec.rb
366
371
  - spec/graphql/schema/printer_spec.rb
@@ -1,20 +0,0 @@
1
- module GraphQL
2
- class Query
3
- # Turn Ruby values into something useful for query execution
4
- class RubyInput
5
- def initialize(type, incoming_value)
6
- @type = type
7
- @incoming_value = incoming_value
8
- end
9
-
10
- def graphql_value
11
- @type.coerce_input!(@incoming_value)
12
- end
13
-
14
- def self.coerce(type, value)
15
- input = self.new(type, value)
16
- input.graphql_value
17
- end
18
- end
19
- end
20
- end