graphql 1.1.0 → 1.2.0
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/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
|