graphql 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/analyze_query.rb +4 -2
- data/lib/graphql/analysis/field_usage.rb +4 -4
- data/lib/graphql/analysis/query_complexity.rb +16 -21
- data/lib/graphql/argument.rb +13 -6
- data/lib/graphql/base_type.rb +2 -1
- data/lib/graphql/compatibility/execution_specification.rb +76 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +16 -2
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -5
- data/lib/graphql/compatibility/schema_parser_specification.rb +6 -0
- data/lib/graphql/define/assign_argument.rb +8 -2
- data/lib/graphql/define/instance_definable.rb +12 -15
- data/lib/graphql/directive.rb +2 -1
- data/lib/graphql/enum_type.rb +5 -7
- data/lib/graphql/field.rb +6 -11
- data/lib/graphql/field/resolve.rb +1 -0
- data/lib/graphql/input_object_type.rb +9 -9
- data/lib/graphql/interface_type.rb +2 -1
- data/lib/graphql/internal_representation.rb +1 -0
- data/lib/graphql/internal_representation/node.rb +31 -9
- data/lib/graphql/internal_representation/rewrite.rb +26 -26
- data/lib/graphql/internal_representation/selections.rb +41 -0
- data/lib/graphql/introspection/input_value_type.rb +6 -2
- data/lib/graphql/language/generation.rb +2 -0
- data/lib/graphql/language/lexer.rl +4 -0
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +525 -509
- data/lib/graphql/language/parser.y +2 -0
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/query.rb +21 -0
- data/lib/graphql/query/context.rb +52 -4
- data/lib/graphql/query/serial_execution.rb +3 -4
- data/lib/graphql/query/serial_execution/field_resolution.rb +35 -36
- data/lib/graphql/query/serial_execution/operation_resolution.rb +9 -15
- data/lib/graphql/query/serial_execution/selection_resolution.rb +14 -11
- data/lib/graphql/query/serial_execution/value_resolution.rb +18 -17
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/relay/mutation.rb +5 -8
- data/lib/graphql/scalar_type.rb +1 -2
- data/lib/graphql/schema.rb +2 -13
- data/lib/graphql/schema/build_from_definition.rb +28 -13
- data/lib/graphql/schema/loader.rb +4 -1
- data/lib/graphql/schema/printer.rb +10 -3
- data/lib/graphql/schema/timeout_middleware.rb +18 -2
- data/lib/graphql/schema/unique_within_type.rb +6 -3
- data/lib/graphql/static_validation/literal_validator.rb +3 -1
- data/lib/graphql/union_type.rb +1 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -0
- data/spec/graphql/analysis/analyze_query_spec.rb +6 -8
- data/spec/graphql/argument_spec.rb +18 -0
- data/spec/graphql/define/assign_argument_spec.rb +48 -0
- data/spec/graphql/define/instance_definable_spec.rb +4 -2
- data/spec/graphql/execution_error_spec.rb +66 -0
- data/spec/graphql/input_object_type_spec.rb +81 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +104 -21
- data/spec/graphql/introspection/input_value_type_spec.rb +43 -6
- data/spec/graphql/introspection/schema_type_spec.rb +1 -0
- data/spec/graphql/introspection/type_type_spec.rb +2 -0
- data/spec/graphql/language/generation_spec.rb +3 -2
- data/spec/graphql/query/arguments_spec.rb +17 -4
- data/spec/graphql/query/context_spec.rb +23 -0
- data/spec/graphql/query/variables_spec.rb +15 -1
- data/spec/graphql/relay/mutation_spec.rb +42 -2
- data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
- data/spec/graphql/schema/loader_spec.rb +59 -1
- data/spec/graphql/schema/printer_spec.rb +2 -0
- data/spec/graphql/schema/reduce_types_spec.rb +1 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +2 -2
- data/spec/graphql/schema/unique_within_type_spec.rb +9 -0
- data/spec/graphql/schema/validation_spec.rb +15 -3
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +122 -0
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +78 -0
- data/spec/support/dairy_app.rb +9 -0
- data/spec/support/minimum_input_object.rb +4 -0
- data/spec/support/star_wars_schema.rb +1 -1
- metadata +5 -5
- data/lib/graphql/query/serial_execution/execution_context.rb +0 -37
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +0 -54
@@ -22,4 +22,22 @@ describe GraphQL::Argument do
|
|
22
22
|
argument = GraphQL::Argument.define(name: :favoriteFood, type: -> { GraphQL::STRING_TYPE })
|
23
23
|
assert_equal GraphQL::STRING_TYPE, argument.type
|
24
24
|
end
|
25
|
+
|
26
|
+
it "accepts a default_value" do
|
27
|
+
argument = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE, default_value: 'Default')
|
28
|
+
assert_equal 'Default', argument.default_value
|
29
|
+
assert argument.default_value?
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts a default_value of nil" do
|
33
|
+
argument = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE, default_value: nil)
|
34
|
+
assert argument.default_value.nil?
|
35
|
+
assert argument.default_value?
|
36
|
+
end
|
37
|
+
|
38
|
+
it "default_value is optional" do
|
39
|
+
argument = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE)
|
40
|
+
assert argument.default_value.nil?
|
41
|
+
assert !argument.default_value?
|
42
|
+
end
|
25
43
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::Define::AssignArgument do
|
4
|
+
it "it accepts default_value" do
|
5
|
+
arg = define_argument(:a, GraphQL::STRING_TYPE, default_value: 'Default')
|
6
|
+
|
7
|
+
assert_equal "Default", arg.default_value
|
8
|
+
assert arg.default_value?
|
9
|
+
end
|
10
|
+
|
11
|
+
it "default_value is optional" do
|
12
|
+
arg = define_argument(:a, GraphQL::STRING_TYPE)
|
13
|
+
|
14
|
+
assert arg.default_value.nil?
|
15
|
+
assert !arg.default_value?
|
16
|
+
end
|
17
|
+
|
18
|
+
it "default_value can be explicitly set to nil" do
|
19
|
+
arg = define_argument(:a, GraphQL::STRING_TYPE, default_value: nil)
|
20
|
+
|
21
|
+
assert arg.default_value.nil?
|
22
|
+
assert arg.default_value?
|
23
|
+
end
|
24
|
+
|
25
|
+
it "passing unknown keyword arguments will raise" do
|
26
|
+
err = assert_raises ArgumentError do
|
27
|
+
define_argument(:a, GraphQL::STRING_TYPE, blah: nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_equal 'unknown keyword: blah', err.message
|
31
|
+
|
32
|
+
err = assert_raises ArgumentError do
|
33
|
+
define_argument(:a, GraphQL::STRING_TYPE, blah: nil, blah2: nil)
|
34
|
+
end
|
35
|
+
|
36
|
+
assert_equal 'unknown keywords: blah, blah2', err.message
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_argument(*args)
|
40
|
+
type = GraphQL::ObjectType.define do
|
41
|
+
field :a, types.String do
|
42
|
+
argument(*args)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
type.fields['a'].arguments[args.first.to_s]
|
47
|
+
end
|
48
|
+
end
|
@@ -11,11 +11,13 @@ module Garden
|
|
11
11
|
|
12
12
|
class Vegetable
|
13
13
|
include GraphQL::Define::InstanceDefinable
|
14
|
-
|
14
|
+
attr_accessor :name, :start_planting_on, :end_planting_on
|
15
|
+
ensure_defined(:name, :start_planting_on, :end_planting_on)
|
15
16
|
accepts_definitions :name, plant_between: DefinePlantBetween, color: GraphQL::Define.assign_metadata_key(:color)
|
16
17
|
|
17
18
|
# definition added later:
|
18
|
-
|
19
|
+
attr_accessor :height
|
20
|
+
ensure_defined(:height)
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -44,6 +44,7 @@ describe GraphQL::ExecutionError do
|
|
44
44
|
}
|
45
45
|
}
|
46
46
|
executionError
|
47
|
+
valueWithExecutionError
|
47
48
|
}
|
48
49
|
|
49
50
|
fragment similarCheeseFields on Cheese {
|
@@ -90,6 +91,7 @@ describe GraphQL::ExecutionError do
|
|
90
91
|
]
|
91
92
|
},
|
92
93
|
"executionError" => nil,
|
94
|
+
"valueWithExecutionError" => 0
|
93
95
|
},
|
94
96
|
"errors"=>[
|
95
97
|
{
|
@@ -127,6 +129,11 @@ describe GraphQL::ExecutionError do
|
|
127
129
|
"locations"=>[{"line"=>41, "column"=>7}],
|
128
130
|
"path"=>["executionError"]
|
129
131
|
},
|
132
|
+
{
|
133
|
+
"message"=>"Could not fetch latest value",
|
134
|
+
"locations"=>[{"line"=>42, "column"=>7}],
|
135
|
+
"path"=>["valueWithExecutionError"]
|
136
|
+
},
|
130
137
|
]
|
131
138
|
}
|
132
139
|
assert_equal(expected_result, result)
|
@@ -185,4 +192,63 @@ describe GraphQL::ExecutionError do
|
|
185
192
|
assert_equal(expected_result, result)
|
186
193
|
end
|
187
194
|
end
|
195
|
+
|
196
|
+
describe "fragment query when returned from a field" do
|
197
|
+
let(:query_string) {%|
|
198
|
+
query MilkQuery {
|
199
|
+
dairy {
|
200
|
+
...Dairy
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
fragment Dairy on Dairy {
|
205
|
+
milks {
|
206
|
+
source
|
207
|
+
executionError
|
208
|
+
allDairy {
|
209
|
+
__typename
|
210
|
+
...Milk
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
fragment Milk on Milk {
|
216
|
+
origin
|
217
|
+
executionError
|
218
|
+
}
|
219
|
+
|}
|
220
|
+
it "the error is inserted into the errors key and the rest of the query is fulfilled" do
|
221
|
+
expected_result = {
|
222
|
+
"data"=>{
|
223
|
+
"dairy" => {
|
224
|
+
"milks" => [
|
225
|
+
{
|
226
|
+
"source" => "COW",
|
227
|
+
"executionError" => nil,
|
228
|
+
"allDairy" => [
|
229
|
+
{ "__typename" => "Cheese" },
|
230
|
+
{ "__typename" => "Cheese" },
|
231
|
+
{ "__typename" => "Cheese" },
|
232
|
+
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil }
|
233
|
+
]
|
234
|
+
}
|
235
|
+
]
|
236
|
+
}
|
237
|
+
},
|
238
|
+
"errors"=>[
|
239
|
+
{
|
240
|
+
"message"=>"There was an execution error",
|
241
|
+
"locations"=>[{"line"=>11, "column"=>9}],
|
242
|
+
"path"=>["dairy", "milks", 0, "executionError"]
|
243
|
+
},
|
244
|
+
{
|
245
|
+
"message"=>"There was an execution error",
|
246
|
+
"locations"=>[{"line"=>21, "column"=>7}],
|
247
|
+
"path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"]
|
248
|
+
}
|
249
|
+
]
|
250
|
+
}
|
251
|
+
assert_equal(expected_result, result)
|
252
|
+
end
|
253
|
+
end
|
188
254
|
end
|
@@ -29,6 +29,32 @@ describe GraphQL::InputObjectType do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
describe "validate_input with null" do
|
33
|
+
let(:schema) { GraphQL::Schema.from_definition(%|
|
34
|
+
type Query {
|
35
|
+
a: Int
|
36
|
+
}
|
37
|
+
|
38
|
+
input ExampleInputObject {
|
39
|
+
a: String
|
40
|
+
b: Int!
|
41
|
+
}
|
42
|
+
|) }
|
43
|
+
let(:input_type) { schema.types['ExampleInputObject'] }
|
44
|
+
|
45
|
+
it "returns an invalid result when value is null for non-null argument" do
|
46
|
+
invalid_input = MinimumInputObject.new({"a" => "Test", "b" => nil})
|
47
|
+
result = input_type.validate_input(invalid_input, PermissiveWarden)
|
48
|
+
assert(!result.valid?)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns valid result when value is null for nullable argument" do
|
52
|
+
invalid_input = MinimumInputObject.new({"a" => nil, "b" => 1})
|
53
|
+
result = input_type.validate_input(invalid_input, PermissiveWarden)
|
54
|
+
assert(result.valid?)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
32
58
|
describe "validate_input with enumerable input" do
|
33
59
|
describe "with good input" do
|
34
60
|
let(:input) do
|
@@ -115,6 +141,61 @@ describe GraphQL::InputObjectType do
|
|
115
141
|
end
|
116
142
|
end
|
117
143
|
|
144
|
+
describe "coerce_result" do
|
145
|
+
it "omits unspecified arguments" do
|
146
|
+
result = input_object.coerce_result(fatContent: 0.3)
|
147
|
+
assert_equal ["fatContent"], result.keys
|
148
|
+
assert_equal 0.3, result["fatContent"]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "coercion of null inputs" do
|
153
|
+
let(:schema) { GraphQL::Schema.from_definition(%|
|
154
|
+
type Query {
|
155
|
+
a: Int
|
156
|
+
}
|
157
|
+
|
158
|
+
input ExampleInputObject {
|
159
|
+
a: String
|
160
|
+
b: Int!
|
161
|
+
c: String = "Default"
|
162
|
+
}
|
163
|
+
|) }
|
164
|
+
let(:input_type) { schema.types['ExampleInputObject'] }
|
165
|
+
|
166
|
+
it "null values are returned in coerced input" do
|
167
|
+
input = MinimumInputObject.new({"a" => "Test", "b" => nil,"c" => "Test"})
|
168
|
+
result = input_type.coerce_input(input)
|
169
|
+
|
170
|
+
assert_equal 'Test', result['a']
|
171
|
+
|
172
|
+
assert result.key?('b')
|
173
|
+
assert_equal nil, result['b']
|
174
|
+
|
175
|
+
assert_equal "Test", result['c']
|
176
|
+
end
|
177
|
+
|
178
|
+
it "null values are preserved when argument has a default value" do
|
179
|
+
input = MinimumInputObject.new({"a" => "Test", "b" => 1, "c" => nil})
|
180
|
+
result = input_type.coerce_input(input)
|
181
|
+
|
182
|
+
assert_equal 'Test', result['a']
|
183
|
+
assert_equal 1, result['b']
|
184
|
+
|
185
|
+
assert result.key?('c')
|
186
|
+
assert_equal nil, result['c']
|
187
|
+
end
|
188
|
+
|
189
|
+
it "omitted arguments are not returned" do
|
190
|
+
input = MinimumInputObject.new({"b" => 1, "c" => "Test"})
|
191
|
+
result = input_type.coerce_input(input)
|
192
|
+
|
193
|
+
assert !result.key?('a')
|
194
|
+
assert_equal 1, result['b']
|
195
|
+
assert_equal 'Test', result['c']
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
118
199
|
describe "when sent into a query" do
|
119
200
|
let(:variables) { {} }
|
120
201
|
let(:result) { DummySchema.execute(query_string, variables: variables) }
|
@@ -19,19 +19,21 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
19
19
|
}
|
20
20
|
}
|
21
21
|
|}
|
22
|
+
|
22
23
|
it "produces a tree of nodes" do
|
23
24
|
op_node = rewrite_result["getCheeses"]
|
24
25
|
|
25
|
-
|
26
|
+
root_children = op_node.typed_children[DairyAppQueryType]
|
27
|
+
assert_equal 2, root_children.length
|
26
28
|
assert_equal DairyAppQueryType, op_node.return_type
|
27
|
-
first_field =
|
28
|
-
assert_equal 3, first_field.
|
29
|
-
assert_equal
|
29
|
+
first_field = root_children.values.first
|
30
|
+
assert_equal 3, first_field.typed_children[CheeseType].length
|
31
|
+
assert_equal DairyAppQueryType, first_field.owner_type
|
30
32
|
assert_equal CheeseType, first_field.return_type
|
31
33
|
|
32
|
-
second_field =
|
33
|
-
assert_equal 1, second_field.
|
34
|
-
assert_equal
|
34
|
+
second_field = root_children.values.last
|
35
|
+
assert_equal 1, second_field.typed_children[CheeseType].length
|
36
|
+
assert_equal DairyAppQueryType.get_field("cheese"), second_field.definition
|
35
37
|
assert_equal CheeseType, second_field.return_type
|
36
38
|
assert second_field.inspect.is_a?(String)
|
37
39
|
end
|
@@ -47,9 +49,9 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
47
49
|
|}
|
48
50
|
|
49
51
|
it "gets dynamic field definitions" do
|
50
|
-
cheese_field = rewrite_result[nil].
|
51
|
-
typename_field = cheese_field.
|
52
|
-
assert_equal "__typename", typename_field.
|
52
|
+
cheese_field = rewrite_result[nil].typed_children[DairyAppQueryType]["cheese"]
|
53
|
+
typename_field = cheese_field.typed_children[CheeseType]["typename"]
|
54
|
+
assert_equal "__typename", typename_field.definition.name
|
53
55
|
assert_equal "__typename", typename_field.definition_name
|
54
56
|
end
|
55
57
|
end
|
@@ -125,22 +127,103 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
125
127
|
it "puts all fragment members as children" do
|
126
128
|
op_node = rewrite_result[nil]
|
127
129
|
|
128
|
-
cheese_field = op_node.
|
129
|
-
assert_equal ["id1", "id2", "fatContent", "
|
130
|
+
cheese_field = op_node.typed_children[DairyAppQueryType]["cheese"]
|
131
|
+
assert_equal ["id1", "id2", "fatContent", "similarCow", "flavor"], cheese_field.typed_children[CheeseType].keys
|
132
|
+
assert_equal ["fatContent", "origin"], cheese_field.typed_children[EdibleInterface].keys
|
130
133
|
# Merge:
|
131
|
-
similar_cow_field = cheese_field.
|
132
|
-
assert_equal ["similarCowSource", "fatContent", "similarCheese", "id"], similar_cow_field.
|
134
|
+
similar_cow_field = cheese_field.typed_children[CheeseType]["similarCow"]
|
135
|
+
assert_equal ["similarCowSource", "fatContent", "similarCheese", "id"], similar_cow_field.typed_children[CheeseType].keys
|
133
136
|
# Deep merge:
|
134
|
-
similar_sheep_field = similar_cow_field.
|
135
|
-
assert_equal ["flavor", "source"], similar_sheep_field.
|
137
|
+
similar_sheep_field = similar_cow_field.typed_children[CheeseType]["similarCheese"]
|
138
|
+
assert_equal ["flavor", "source"], similar_sheep_field.typed_children[CheeseType].keys
|
139
|
+
|
140
|
+
edible_origin_node = cheese_field.typed_children[EdibleInterface]["origin"]
|
141
|
+
assert_equal EdibleInterface.get_field("origin"), edible_origin_node.definition
|
142
|
+
assert_equal EdibleInterface, edible_origin_node.owner_type
|
143
|
+
|
144
|
+
edible_fat_content_node = cheese_field.typed_children[EdibleInterface]["fatContent"]
|
145
|
+
assert_equal EdibleInterface.get_field("fatContent"), edible_fat_content_node.definition
|
146
|
+
assert_equal EdibleInterface, edible_fat_content_node.owner_type
|
147
|
+
|
148
|
+
cheese_fat_content_node = cheese_field.typed_children[CheeseType]["fatContent"]
|
149
|
+
assert_equal CheeseType.get_field("fatContent"), cheese_fat_content_node.definition
|
150
|
+
assert_equal CheeseType, cheese_fat_content_node.owner_type
|
151
|
+
|
152
|
+
cheese_flavor_node = cheese_field.typed_children[CheeseType]["flavor"]
|
153
|
+
assert_equal CheeseType.get_field("flavor"), cheese_flavor_node.definition
|
154
|
+
assert_equal CheeseType, cheese_flavor_node.owner_type
|
136
155
|
|
137
|
-
assert_equal [EdibleInterface], cheese_field.children["origin"].definitions.keys
|
138
|
-
assert_equal [CheeseType, EdibleInterface], cheese_field.children["fatContent"].definitions.keys
|
139
|
-
assert_equal [CheeseType], cheese_field.children["flavor"].definitions.keys
|
140
156
|
|
141
157
|
# nested spread inside fragment definition:
|
142
|
-
cheese_2_field = op_node.
|
143
|
-
assert_equal ["id", "fatContent"], cheese_2_field.
|
158
|
+
cheese_2_field = op_node.typed_children[DairyAppQueryType]["cheese2"].typed_children[CheeseType]["similarCheese"]
|
159
|
+
assert_equal ["id", "fatContent"], cheese_2_field.typed_children[CheeseType].keys
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "nested fields on typed fragments" do
|
164
|
+
let(:result) { DummySchema.execute(query_string) }
|
165
|
+
let(:query_string) {%|
|
166
|
+
{
|
167
|
+
allDairy {
|
168
|
+
__typename
|
169
|
+
|
170
|
+
... on Milk {
|
171
|
+
selfAsEdible {
|
172
|
+
milkInlineOrigin: origin
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
... on Cheese {
|
177
|
+
selfAsEdible {
|
178
|
+
cheeseInlineOrigin: origin
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
... on Edible {
|
183
|
+
selfAsEdible {
|
184
|
+
edibleInlineOrigin: origin
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
... {
|
189
|
+
... on Edible {
|
190
|
+
selfAsEdible {
|
191
|
+
untypedInlineOrigin: origin
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
...milkFields
|
196
|
+
...cheeseFields
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
fragment cheeseFields on Cheese {
|
201
|
+
selfAsEdible {
|
202
|
+
cheeseFragmentOrigin: origin
|
203
|
+
}
|
204
|
+
}
|
205
|
+
fragment milkFields on Milk {
|
206
|
+
selfAsEdible {
|
207
|
+
milkFragmentOrigin: origin
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|}
|
211
|
+
|
212
|
+
it "distinguishes between nested fields with the same name on different typed fragments" do
|
213
|
+
all_dairy = result["data"]["allDairy"]
|
214
|
+
cheeses = all_dairy.select { |d| d["__typename"] == "Cheese" }
|
215
|
+
milks = all_dairy.select { |d| d["__typename"] == "Milk" }
|
216
|
+
|
217
|
+
# Make sure all the data is there:
|
218
|
+
assert_equal 3, cheeses.length
|
219
|
+
assert_equal 1, milks.length
|
220
|
+
|
221
|
+
cheeses.each do |cheese|
|
222
|
+
assert_equal ["cheeseInlineOrigin", "cheeseFragmentOrigin", "edibleInlineOrigin", "untypedInlineOrigin"], cheese["selfAsEdible"].keys
|
223
|
+
end
|
224
|
+
milks.each do |milk|
|
225
|
+
assert_equal ["milkInlineOrigin", "milkFragmentOrigin", "edibleInlineOrigin", "untypedInlineOrigin"], milk["selfAsEdible"].keys
|
226
|
+
end
|
144
227
|
end
|
145
228
|
end
|
146
229
|
end
|
@@ -5,19 +5,19 @@ describe GraphQL::Introspection::InputValueType do
|
|
5
5
|
let(:query_string) {%|
|
6
6
|
{
|
7
7
|
__type(name: "DairyProductInput") {
|
8
|
-
name
|
9
|
-
description
|
10
|
-
kind
|
8
|
+
name
|
9
|
+
description
|
10
|
+
kind
|
11
11
|
inputFields {
|
12
|
-
name
|
13
|
-
type { kind, name }
|
12
|
+
name
|
13
|
+
type { kind, name }
|
14
14
|
defaultValue
|
15
15
|
description
|
16
16
|
}
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|}
|
20
|
-
let(:result) { DummySchema.execute(query_string)}
|
20
|
+
let(:result) { DummySchema.execute(query_string) }
|
21
21
|
|
22
22
|
it "exposes metadata about input objects, giving extra quotes for strings" do
|
23
23
|
expected = { "data" => {
|
@@ -62,4 +62,41 @@ describe GraphQL::Introspection::InputValueType do
|
|
62
62
|
|
63
63
|
assert_equal('["COW"]', arg['defaultValue'])
|
64
64
|
end
|
65
|
+
|
66
|
+
it "supports null default values" do
|
67
|
+
schema = GraphQL::Schema.from_definition(%|
|
68
|
+
type Query {
|
69
|
+
hello(person: Person): String
|
70
|
+
}
|
71
|
+
|
72
|
+
input Person {
|
73
|
+
firstName: String!
|
74
|
+
lastName: String = null
|
75
|
+
}
|
76
|
+
|)
|
77
|
+
|
78
|
+
result = schema.execute(%|
|
79
|
+
{
|
80
|
+
__type(name: "Person") {
|
81
|
+
inputFields {
|
82
|
+
name
|
83
|
+
defaultValue
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|)
|
88
|
+
|
89
|
+
expected = {
|
90
|
+
"data" => {
|
91
|
+
"__type" => {
|
92
|
+
"inputFields" => [
|
93
|
+
{ "name" => "firstName", "defaultValue" => nil},
|
94
|
+
{ "name" => "lastName", "defaultValue" => "null"}
|
95
|
+
]
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
assert_equal expected, result
|
101
|
+
end
|
65
102
|
end
|