graphql 1.5.4 → 1.5.5
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 +4 -4
- data/lib/graphql/define/instance_definable.rb +54 -29
- data/lib/graphql/enum_type.rb +0 -1
- data/lib/graphql/execution/execute.rb +15 -9
- data/lib/graphql/execution/field_result.rb +3 -6
- data/lib/graphql/execution/lazy/lazy_method_map.rb +66 -12
- data/lib/graphql/language.rb +15 -1
- data/lib/graphql/language/nodes.rb +17 -1
- data/lib/graphql/language/parser.rb +8 -8
- data/lib/graphql/language/parser.y +8 -8
- data/lib/graphql/query.rb +3 -4
- data/lib/graphql/query/literal_input.rb +2 -0
- data/lib/graphql/relay/mutation.rb +2 -3
- data/lib/graphql/schema.rb +1 -3
- data/lib/graphql/schema/loader.rb +26 -4
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +32 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +11 -5
- data/lib/graphql/static_validation/validation_context.rb +0 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/define/instance_definable_spec.rb +21 -0
- data/spec/graphql/execution/execute_spec.rb +61 -0
- data/spec/graphql/execution/lazy/lazy_method_map_spec.rb +57 -0
- data/spec/graphql/input_object_type_spec.rb +2 -2
- data/spec/graphql/introspection/input_value_type_spec.rb +3 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -0
- data/spec/graphql/query_spec.rb +69 -10
- data/spec/graphql/schema/loader_spec.rb +7 -3
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +0 -1
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +6 -6
- data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +28 -0
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +17 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/dummy/schema.rb +11 -0
- data/spec/support/star_wars/data.rb +16 -2
- metadata +21 -30
@@ -265,7 +265,7 @@ rule
|
|
265
265
|
| directive_definition
|
266
266
|
|
267
267
|
schema_definition:
|
268
|
-
SCHEMA LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, val[2]) }
|
268
|
+
SCHEMA LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, position_source: val[0], **val[2]) }
|
269
269
|
|
270
270
|
operation_type_definition_list:
|
271
271
|
operation_type_definition
|
@@ -282,11 +282,11 @@ rule
|
|
282
282
|
| enum_type_definition
|
283
283
|
| input_object_type_definition
|
284
284
|
|
285
|
-
scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0])) }
|
285
|
+
scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]), position_source: val[0]) }
|
286
286
|
|
287
287
|
object_type_definition:
|
288
288
|
TYPE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY {
|
289
|
-
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]))
|
289
|
+
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]), position_source: val[0])
|
290
290
|
}
|
291
291
|
|
292
292
|
implements_opt:
|
@@ -317,7 +317,7 @@ rule
|
|
317
317
|
|
318
318
|
interface_type_definition:
|
319
319
|
INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY {
|
320
|
-
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
|
320
|
+
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
321
321
|
}
|
322
322
|
|
323
323
|
union_members:
|
@@ -326,22 +326,22 @@ rule
|
|
326
326
|
|
327
327
|
union_type_definition:
|
328
328
|
UNION name directives_list_opt EQUALS union_members {
|
329
|
-
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]))
|
329
|
+
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]), position_source: val[0])
|
330
330
|
}
|
331
331
|
|
332
332
|
enum_type_definition:
|
333
333
|
ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY {
|
334
|
-
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]))
|
334
|
+
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]), position_source: val[0])
|
335
335
|
}
|
336
336
|
|
337
337
|
input_object_type_definition:
|
338
338
|
INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY {
|
339
|
-
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
|
339
|
+
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
340
340
|
}
|
341
341
|
|
342
342
|
directive_definition:
|
343
343
|
DIRECTIVE DIR_SIGN name arguments_definitions_opt ON directive_locations {
|
344
|
-
return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]))
|
344
|
+
return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]), position_source: val[0])
|
345
345
|
}
|
346
346
|
|
347
347
|
directive_locations:
|
data/lib/graphql/query.rb
CHANGED
@@ -65,12 +65,11 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
|
67
67
|
@document && @document.definitions.each do |part|
|
68
|
-
|
68
|
+
case part
|
69
|
+
when GraphQL::Language::Nodes::FragmentDefinition
|
69
70
|
@fragments[part.name] = part
|
70
|
-
|
71
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
71
72
|
@operations[part.name] = part
|
72
|
-
else
|
73
|
-
raise GraphQL::ExecutionError, "GraphQL query cannot contain a schema definition"
|
74
73
|
end
|
75
74
|
end
|
76
75
|
|
@@ -169,9 +169,8 @@ module GraphQL
|
|
169
169
|
input_field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
|
170
170
|
relay_mutation.arguments.each do |input_field_name, field_obj|
|
171
171
|
kwargs = {}
|
172
|
-
if field_obj.default_value?
|
173
|
-
|
174
|
-
end
|
172
|
+
kwargs[:default_value] = field_obj.default_value if field_obj.default_value?
|
173
|
+
kwargs[:as] = field_obj.as
|
175
174
|
input_field(input_field_name, field_obj.type, field_obj.description, **kwargs)
|
176
175
|
end
|
177
176
|
mutation(relay_mutation)
|
data/lib/graphql/schema.rb
CHANGED
@@ -126,8 +126,7 @@ module GraphQL
|
|
126
126
|
|
127
127
|
@possible_types = GraphQL::Schema::PossibleTypes.new(self)
|
128
128
|
|
129
|
-
@lazy_methods =
|
130
|
-
other.lazy_methods.each { |lazy_class, lazy_method| @lazy_methods.set(lazy_class, lazy_method) }
|
129
|
+
@lazy_methods = other.lazy_methods.dup
|
131
130
|
|
132
131
|
@instrumenters = Hash.new { |h, k| h[k] = [] }
|
133
132
|
other.instrumenters.each do |key, insts|
|
@@ -235,7 +234,6 @@ module GraphQL
|
|
235
234
|
@possible_types.possible_types(type_defn)
|
236
235
|
end
|
237
236
|
|
238
|
-
|
239
237
|
# @see [GraphQL::Schema::Warden] Resticted access to root types
|
240
238
|
# @return [GraphQL::ObjectType, nil]
|
241
239
|
def root_type_for_operation(operation)
|
@@ -109,10 +109,32 @@ module GraphQL
|
|
109
109
|
kwargs = {}
|
110
110
|
if type["defaultValue"]
|
111
111
|
kwargs[:default_value] = begin
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
112
|
+
default_value_str = type["defaultValue"]
|
113
|
+
|
114
|
+
dummy_query_str = "query getStuff($var: InputObj = #{default_value_str}) { __typename }"
|
115
|
+
|
116
|
+
# Returns a `GraphQL::Language::Nodes::Document`:
|
117
|
+
dummy_query_ast = GraphQL.parse(dummy_query_str)
|
118
|
+
|
119
|
+
# Reach into the AST for the default value:
|
120
|
+
input_value_ast = dummy_query_ast.definitions.first.variables.first.default_value
|
121
|
+
|
122
|
+
case input_value_ast
|
123
|
+
when String, Integer, Float, TrueClass, FalseClass, Array
|
124
|
+
input_value_ast
|
125
|
+
when GraphQL::Language::Nodes::Enum
|
126
|
+
input_value_ast.name
|
127
|
+
when GraphQL::Language::Nodes::NullValue
|
128
|
+
nil
|
129
|
+
when GraphQL::Language::Nodes::InputObject
|
130
|
+
input_value_ast.to_h
|
131
|
+
else
|
132
|
+
raise(
|
133
|
+
"Encountered unexpected type when loading default value. "\
|
134
|
+
"input_value_ast.class is #{input_value_ast.class} "\
|
135
|
+
"default_value is #{default_value_str}"
|
136
|
+
)
|
137
|
+
end
|
116
138
|
end
|
117
139
|
end
|
118
140
|
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
8
8
|
# don't exist, etc.
|
9
9
|
ALL_RULES = [
|
10
|
+
GraphQL::StaticValidation::NoDefinitionsArePresent,
|
10
11
|
GraphQL::StaticValidation::DirectivesAreDefined,
|
11
12
|
GraphQL::StaticValidation::DirectivesAreInValidLocations,
|
12
13
|
GraphQL::StaticValidation::UniqueDirectivesPerLocation,
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class NoDefinitionsArePresent
|
5
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
6
|
+
|
7
|
+
def validate(context)
|
8
|
+
schema_definition_nodes = []
|
9
|
+
register_node = ->(node, _p) {
|
10
|
+
schema_definition_nodes << node
|
11
|
+
GraphQL::Language::Visitor::SKIP
|
12
|
+
}
|
13
|
+
|
14
|
+
visitor = context.visitor
|
15
|
+
visitor[GraphQL::Language::Nodes::DirectiveDefinition] << register_node
|
16
|
+
visitor[GraphQL::Language::Nodes::SchemaDefinition] << register_node
|
17
|
+
visitor[GraphQL::Language::Nodes::ScalarTypeDefinition] << register_node
|
18
|
+
visitor[GraphQL::Language::Nodes::ObjectTypeDefinition] << register_node
|
19
|
+
visitor[GraphQL::Language::Nodes::InputObjectTypeDefinition] << register_node
|
20
|
+
visitor[GraphQL::Language::Nodes::InterfaceTypeDefinition] << register_node
|
21
|
+
visitor[GraphQL::Language::Nodes::UnionTypeDefinition] << register_node
|
22
|
+
visitor[GraphQL::Language::Nodes::EnumTypeDefinition] << register_node
|
23
|
+
|
24
|
+
visitor[GraphQL::Language::Nodes::Document].leave << ->(node, _p) {
|
25
|
+
if schema_definition_nodes.any?
|
26
|
+
context.errors << message(%|Query cannot contain schema definitions|, schema_definition_nodes, context: context)
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -14,19 +14,25 @@ module GraphQL
|
|
14
14
|
|
15
15
|
context.visitor[GraphQL::Language::Nodes::Argument] << ->(node, parent) {
|
16
16
|
return if !node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
17
|
-
|
17
|
+
arguments = nil
|
18
|
+
|
19
|
+
case parent
|
20
|
+
when GraphQL::Language::Nodes::Field
|
18
21
|
arguments = context.field_definition.arguments
|
19
|
-
|
22
|
+
when GraphQL::Language::Nodes::Directive
|
20
23
|
arguments = context.directive_definition.arguments
|
21
|
-
|
22
|
-
|
24
|
+
when GraphQL::Language::Nodes::InputObject
|
25
|
+
arg_type = context.argument_definition.type.unwrap
|
26
|
+
if arg_type.is_a?(GraphQL::InputObjectType)
|
27
|
+
arguments = arg_type.input_fields
|
28
|
+
end
|
23
29
|
else
|
24
30
|
raise("Unexpected argument parent: #{parent}")
|
25
31
|
end
|
26
32
|
var_defn_ast = declared_variables[node.value.name]
|
27
33
|
# Might be undefined :(
|
28
34
|
# VariablesAreUsedAndDefined can't finalize its search until the end of the document.
|
29
|
-
var_defn_ast && validate_usage(arguments, node, var_defn_ast, context)
|
35
|
+
var_defn_ast && arguments && validate_usage(arguments, node, var_defn_ast, context)
|
30
36
|
}
|
31
37
|
end
|
32
38
|
|
data/lib/graphql/version.rb
CHANGED
@@ -78,6 +78,27 @@ describe GraphQL::Define::InstanceDefinable do
|
|
78
78
|
okra.define { name "Okra" }
|
79
79
|
assert_equal "Okra", okra.name
|
80
80
|
end
|
81
|
+
|
82
|
+
describe "errors in define blocks" do
|
83
|
+
it "preserves the definition block to try again" do
|
84
|
+
magic_number = 12
|
85
|
+
|
86
|
+
radish = Garden::Vegetable.define {
|
87
|
+
name "Pre-error"
|
88
|
+
magic_number += 1
|
89
|
+
if magic_number == 13
|
90
|
+
raise "👻"
|
91
|
+
end
|
92
|
+
name "Radish"
|
93
|
+
}
|
94
|
+
|
95
|
+
# The first call triggers an error:
|
96
|
+
assert_raises(RuntimeError) { radish.name }
|
97
|
+
# Calling definintion-dependent method should re-run the block,
|
98
|
+
# not leave old values around:
|
99
|
+
assert_equal "Radish", radish.name
|
100
|
+
end
|
101
|
+
end
|
81
102
|
end
|
82
103
|
|
83
104
|
describe "#redefine" do
|
@@ -76,4 +76,65 @@ describe GraphQL::Execution::Execute do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
+
|
80
|
+
describe "when a list member raises an error" do
|
81
|
+
let(:schema) {
|
82
|
+
thing_type = GraphQL::ObjectType.define do
|
83
|
+
name "Thing"
|
84
|
+
field :name, !types.String do
|
85
|
+
resolve ->(o, a, c) {
|
86
|
+
-> {
|
87
|
+
raise GraphQL::ExecutionError.new("👻")
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
query_type = GraphQL::ObjectType.define do
|
94
|
+
name "Query"
|
95
|
+
field :things, !types[!thing_type] do
|
96
|
+
resolve ->(o, a, c) {
|
97
|
+
[OpenStruct.new(name: "A")]
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
field :nullableThings, !types[thing_type] do
|
102
|
+
resolve ->(o, a, c) {
|
103
|
+
[OpenStruct.new(name: "A")]
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
GraphQL::Schema.define do
|
109
|
+
query query_type
|
110
|
+
lazy_resolve(Proc, :call)
|
111
|
+
end
|
112
|
+
}
|
113
|
+
|
114
|
+
it "handles the error & propagates the null" do
|
115
|
+
res = schema.execute <<-GRAPHQL
|
116
|
+
{
|
117
|
+
things {
|
118
|
+
name
|
119
|
+
}
|
120
|
+
}
|
121
|
+
GRAPHQL
|
122
|
+
|
123
|
+
assert_equal nil, res["data"]
|
124
|
+
assert_equal "👻", res["errors"].first["message"]
|
125
|
+
end
|
126
|
+
|
127
|
+
it "allows nulls" do
|
128
|
+
res = schema.execute <<-GRAPHQL
|
129
|
+
{
|
130
|
+
nullableThings {
|
131
|
+
name
|
132
|
+
}
|
133
|
+
}
|
134
|
+
GRAPHQL
|
135
|
+
|
136
|
+
assert_equal [nil], res["data"]["nullableThings"]
|
137
|
+
assert_equal "👻", res["errors"].first["message"]
|
138
|
+
end
|
139
|
+
end
|
79
140
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe GraphQL::Execution::Lazy::LazyMethodMap do
|
5
|
+
def self.test_lazy_method_map
|
6
|
+
it "handles multithreaded access" do
|
7
|
+
a = Class.new
|
8
|
+
b = Class.new(a)
|
9
|
+
c = Class.new(b)
|
10
|
+
lazy_method_map.set(a, :a)
|
11
|
+
threads = 1000.times.map do |i|
|
12
|
+
Thread.new {
|
13
|
+
d = Class.new(c)
|
14
|
+
assert_equal :a, lazy_method_map.get(d.new)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
threads.map(&:join)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "dups" do
|
21
|
+
a = Class.new
|
22
|
+
b = Class.new(a)
|
23
|
+
c = Class.new(b)
|
24
|
+
lazy_method_map.set(a, :a)
|
25
|
+
lazy_method_map.get(b.new)
|
26
|
+
lazy_method_map.get(c.new)
|
27
|
+
|
28
|
+
dup_map = lazy_method_map.dup
|
29
|
+
assert_equal 3, dup_map.instance_variable_get(:@storage).size
|
30
|
+
assert_equal :a, dup_map.get(a.new)
|
31
|
+
assert_equal :a, dup_map.get(b.new)
|
32
|
+
assert_equal :a, dup_map.get(c.new)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with a plain hash" do
|
37
|
+
let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: false) }
|
38
|
+
test_lazy_method_map
|
39
|
+
|
40
|
+
it "has a Ruby Hash inside" do
|
41
|
+
storage = lazy_method_map
|
42
|
+
.instance_variable_get(:@storage)
|
43
|
+
.instance_variable_get(:@storage)
|
44
|
+
assert_instance_of Hash, storage
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "with a Concurrent::Map" do
|
49
|
+
let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: true) }
|
50
|
+
test_lazy_method_map
|
51
|
+
|
52
|
+
it "has a Concurrent::Map inside" do
|
53
|
+
storage = lazy_method_map.instance_variable_get(:@storage)
|
54
|
+
assert_instance_of Concurrent::Map, storage
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -325,8 +325,8 @@ describe GraphQL::InputObjectType do
|
|
325
325
|
it "shallow-copies internal state" do
|
326
326
|
input_object_2 = input_object.dup
|
327
327
|
input_object_2.arguments["nonsense"] = GraphQL::Argument.define(name: "int", type: GraphQL::INT_TYPE)
|
328
|
-
assert_equal
|
329
|
-
assert_equal
|
328
|
+
assert_equal 5, input_object.arguments.size
|
329
|
+
assert_equal 6, input_object_2.arguments.size
|
330
330
|
end
|
331
331
|
end
|
332
332
|
end
|
@@ -34,7 +34,9 @@ describe GraphQL::Introspection::InputValueType do
|
|
34
34
|
{"name"=>"fatContent", "type"=>{"kind"=>"SCALAR", "name" => "Float"}, "defaultValue"=>"0.3",
|
35
35
|
"description" => "How much fat it has"},
|
36
36
|
{"name"=>"organic", "type"=>{"kind"=>"SCALAR", "name" => "Boolean"}, "defaultValue"=>"false",
|
37
|
-
"description" => nil}
|
37
|
+
"description" => nil},
|
38
|
+
{"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction:\"ASC\"}",
|
39
|
+
"description" => nil},
|
38
40
|
]
|
39
41
|
}
|
40
42
|
}}
|
@@ -122,6 +122,7 @@ describe GraphQL::Introspection::TypeType do
|
|
122
122
|
{"name"=>"originDairy", "type"=>{"kind"=>"SCALAR","name"=>"String"}, "defaultValue"=>"\"Sugar Hollow Dairy\""},
|
123
123
|
{"name"=>"fatContent", "type"=>{"kind"=>"SCALAR","name" => "Float"}, "defaultValue"=>"0.3"},
|
124
124
|
{"name"=>"organic", "type"=>{"kind"=>"SCALAR","name" => "Boolean"}, "defaultValue"=>"false"},
|
125
|
+
{"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction:\"ASC\"}"},
|
125
126
|
]
|
126
127
|
}
|
127
128
|
}}
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -143,16 +143,6 @@ describe GraphQL::Query do
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
it "fails to execute a query containing a type definition" do
|
147
|
-
query_string = '
|
148
|
-
{ root }
|
149
|
-
|
150
|
-
type Query { foo: String }
|
151
|
-
'
|
152
|
-
exc = assert_raises(GraphQL::ExecutionError) { GraphQL::Query.new(schema, query_string) }
|
153
|
-
assert_equal "GraphQL query cannot contain a schema definition", exc.message
|
154
|
-
end
|
155
|
-
|
156
146
|
it "uses root_value as the object for the root type" do
|
157
147
|
result = GraphQL::Query.new(schema, '{ root }', root_value: "I am root").result
|
158
148
|
assert_equal 'I am root', result.fetch('data').fetch('root')
|
@@ -470,4 +460,73 @@ describe GraphQL::Query do
|
|
470
460
|
}
|
471
461
|
end
|
472
462
|
end
|
463
|
+
|
464
|
+
describe 'NullValue type arguments' do
|
465
|
+
let(:schema_definition) {
|
466
|
+
<<-GRAPHQL
|
467
|
+
type Query {
|
468
|
+
foo(id: [ID]): Int
|
469
|
+
}
|
470
|
+
GRAPHQL
|
471
|
+
}
|
472
|
+
let(:expected_args) { [] }
|
473
|
+
let(:default_resolver) do
|
474
|
+
{
|
475
|
+
'Query' => { 'foo' => ->(_obj, args, _ctx) { expected_args.push(args); 1 } },
|
476
|
+
}
|
477
|
+
end
|
478
|
+
let(:schema) { GraphQL::Schema.from_definition(schema_definition, default_resolve: default_resolver) }
|
479
|
+
|
480
|
+
it 'sets argument to nil when null is passed' do
|
481
|
+
query = <<-GRAPHQL
|
482
|
+
{
|
483
|
+
foo(id: null)
|
484
|
+
}
|
485
|
+
GRAPHQL
|
486
|
+
|
487
|
+
schema.execute(query)
|
488
|
+
|
489
|
+
assert(expected_args.first.key?('id'))
|
490
|
+
assert_nil(expected_args.first['id'])
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'sets argument to nil when nil is passed via variable' do
|
494
|
+
query = <<-GRAPHQL
|
495
|
+
query baz($id: [ID]) {
|
496
|
+
foo(id: $id)
|
497
|
+
}
|
498
|
+
GRAPHQL
|
499
|
+
|
500
|
+
schema.execute(query, variables: { 'id' => nil })
|
501
|
+
|
502
|
+
assert(expected_args.first.key?('id'))
|
503
|
+
assert([nil], expected_args.first['id'])
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'sets argument to [nil] when [null] is passed' do
|
507
|
+
query = <<-GRAPHQL
|
508
|
+
{
|
509
|
+
foo(id: [null])
|
510
|
+
}
|
511
|
+
GRAPHQL
|
512
|
+
|
513
|
+
schema.execute(query)
|
514
|
+
|
515
|
+
assert(expected_args.first.key?('id'))
|
516
|
+
assert_equal([nil], expected_args.first['id'])
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'sets argument to [nil] when [nil] is passed via variable' do
|
520
|
+
query = <<-GRAPHQL
|
521
|
+
query baz($id: [ID]) {
|
522
|
+
foo(id: $id)
|
523
|
+
}
|
524
|
+
GRAPHQL
|
525
|
+
|
526
|
+
schema.execute(query, variables: { 'id' => [nil] })
|
527
|
+
|
528
|
+
assert(expected_args.first.key?('id'))
|
529
|
+
assert_equal([nil], expected_args.first['id'])
|
530
|
+
end
|
531
|
+
end
|
473
532
|
end
|