graphql 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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