graphql 1.0.0 → 1.1.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.rb +10 -0
- data/lib/graphql/base_type.rb +8 -5
- data/lib/graphql/compatibility.rb +3 -0
- data/lib/graphql/compatibility/execution_specification.rb +414 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +81 -0
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +78 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +239 -0
- data/lib/graphql/define/instance_definable.rb +53 -21
- data/lib/graphql/directive.rb +1 -1
- data/lib/graphql/enum_type.rb +31 -8
- data/lib/graphql/execution/directive_checks.rb +0 -6
- data/lib/graphql/input_object_type.rb +6 -4
- data/lib/graphql/introspection/arguments_field.rb +3 -1
- data/lib/graphql/introspection/enum_values_field.rb +10 -5
- data/lib/graphql/introspection/fields_field.rb +1 -1
- data/lib/graphql/introspection/input_fields_field.rb +2 -2
- data/lib/graphql/introspection/interfaces_field.rb +7 -1
- data/lib/graphql/introspection/possible_types_field.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/introspection/type_by_name_field.rb +4 -2
- data/lib/graphql/introspection/type_type.rb +7 -6
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language/parser.y +1 -1
- data/lib/graphql/list_type.rb +3 -4
- data/lib/graphql/non_null_type.rb +4 -8
- data/lib/graphql/object_type.rb +5 -3
- data/lib/graphql/query.rb +48 -12
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/serial_execution/execution_context.rb +8 -3
- data/lib/graphql/query/serial_execution/field_resolution.rb +8 -5
- data/lib/graphql/query/serial_execution/operation_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -21
- data/lib/graphql/query/serial_execution/value_resolution.rb +59 -99
- data/lib/graphql/query/variables.rb +7 -2
- data/lib/graphql/scalar_type.rb +1 -1
- data/lib/graphql/schema.rb +49 -18
- data/lib/graphql/schema/build_from_definition.rb +248 -0
- data/lib/graphql/schema/instrumented_field_map.rb +23 -0
- data/lib/graphql/schema/loader.rb +4 -11
- data/lib/graphql/schema/possible_types.rb +4 -2
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +4 -4
- data/lib/graphql/schema/type_map.rb +1 -1
- data/lib/graphql/schema/validation.rb +4 -0
- data/lib/graphql/schema/warden.rb +114 -0
- data/lib/graphql/static_validation/literal_validator.rb +10 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -3
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -14
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -4
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +2 -1
- data/lib/graphql/static_validation/validation_context.rb +7 -1
- data/lib/graphql/union_type.rb +6 -3
- data/lib/graphql/unresolved_type_error.rb +1 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -5
- data/spec/graphql/compatibility/execution_specification_spec.rb +3 -0
- data/spec/graphql/compatibility/query_parser_specification_spec.rb +5 -0
- data/spec/graphql/compatibility/schema_parser_specification_spec.rb +5 -0
- data/spec/graphql/define/instance_definable_spec.rb +20 -0
- data/spec/graphql/directive_spec.rb +11 -0
- data/spec/graphql/enum_type_spec.rb +20 -1
- data/spec/graphql/input_object_type_spec.rb +9 -9
- data/spec/graphql/introspection/directive_type_spec.rb +4 -4
- data/spec/graphql/introspection/input_value_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +28 -26
- data/spec/graphql/language/parser_spec.rb +27 -17
- data/spec/graphql/list_type_spec.rb +2 -2
- data/spec/graphql/query/variables_spec.rb +1 -0
- data/spec/graphql/scalar_type_spec.rb +3 -3
- data/spec/graphql/schema/build_from_definition_spec.rb +693 -0
- data/spec/graphql/schema/type_expression_spec.rb +3 -3
- data/spec/graphql/schema/validation_spec.rb +7 -3
- data/spec/graphql/schema/warden_spec.rb +510 -0
- data/spec/graphql/schema_spec.rb +129 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -1
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -3
- data/spec/spec_helper.rb +27 -1
- data/spec/support/dairy_app.rb +8 -5
- metadata +21 -3
- data/lib/graphql/language/parser_tests.rb +0 -809
data/spec/graphql/schema_spec.rb
CHANGED
@@ -124,4 +124,133 @@ describe GraphQL::Schema do
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
end
|
127
|
+
|
128
|
+
describe "directives" do
|
129
|
+
describe "when directives are not overwritten" do
|
130
|
+
it "contains built-in directives" do
|
131
|
+
schema = GraphQL::Schema.define
|
132
|
+
|
133
|
+
assert_equal ['deprecated', 'include', 'skip'], schema.directives.keys.sort
|
134
|
+
|
135
|
+
assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
|
136
|
+
assert_equal GraphQL::Directive::IncludeDirective, schema.directives['include']
|
137
|
+
assert_equal GraphQL::Directive::SkipDirective, schema.directives['skip']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "when directives are overwritten" do
|
142
|
+
it "contains only specified directives" do
|
143
|
+
schema = GraphQL::Schema.define do
|
144
|
+
directives [GraphQL::Directive::DeprecatedDirective]
|
145
|
+
end
|
146
|
+
|
147
|
+
assert_equal ['deprecated'], schema.directives.keys.sort
|
148
|
+
assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe ".from_definition" do
|
154
|
+
it "uses BuildFromSchema to build a schema from a definition string" do
|
155
|
+
schema = <<-SCHEMA
|
156
|
+
type Query {
|
157
|
+
str: String
|
158
|
+
}
|
159
|
+
SCHEMA
|
160
|
+
|
161
|
+
built_schema = GraphQL::Schema.from_definition(schema)
|
162
|
+
assert_equal schema.chop, GraphQL::Schema::Printer.print_schema(built_schema)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe ".from_introspection" do
|
167
|
+
let(:schema) {
|
168
|
+
query_root = GraphQL::ObjectType.define do
|
169
|
+
name 'Query'
|
170
|
+
field :str, types.String
|
171
|
+
end
|
172
|
+
|
173
|
+
GraphQL::Schema.define do
|
174
|
+
query query_root
|
175
|
+
end
|
176
|
+
}
|
177
|
+
let(:schema_json) {
|
178
|
+
schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
|
179
|
+
}
|
180
|
+
it "uses Schema::Loader to build a schema from an introspection result" do
|
181
|
+
built_schema = GraphQL::Schema.from_introspection(schema_json)
|
182
|
+
assert_equal GraphQL::Schema::Printer.print_schema(schema), GraphQL::Schema::Printer.print_schema(built_schema)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#instrument" do
|
187
|
+
class MultiplyInstrumenter
|
188
|
+
def initialize(multiplier)
|
189
|
+
@multiplier = multiplier
|
190
|
+
end
|
191
|
+
|
192
|
+
def instrument(type_defn, field_defn)
|
193
|
+
if type_defn.name == "Query" && field_defn.name == "int"
|
194
|
+
prev_proc = field_defn.resolve_proc
|
195
|
+
new_resolve_proc = ->(obj, args, ctx) {
|
196
|
+
inner_value = prev_proc.call(obj, args, ctx)
|
197
|
+
inner_value * @multiplier
|
198
|
+
}
|
199
|
+
|
200
|
+
field_defn.redefine do
|
201
|
+
resolve(new_resolve_proc)
|
202
|
+
end
|
203
|
+
else
|
204
|
+
field_defn
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class VariableCountInstrumenter
|
210
|
+
attr_reader :counts
|
211
|
+
def initialize
|
212
|
+
@counts = []
|
213
|
+
end
|
214
|
+
|
215
|
+
def before_query(query)
|
216
|
+
@counts << query.variables.length
|
217
|
+
end
|
218
|
+
|
219
|
+
def after_query(query)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
let(:variable_counter) {
|
224
|
+
VariableCountInstrumenter.new
|
225
|
+
}
|
226
|
+
let(:query_type) {
|
227
|
+
GraphQL::ObjectType.define do
|
228
|
+
name "Query"
|
229
|
+
field :int, types.Int do
|
230
|
+
argument :value, types.Int
|
231
|
+
resolve -> (obj, args, ctx) { args[:value] }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
let(:schema) {
|
237
|
+
spec = self
|
238
|
+
GraphQL::Schema.define do
|
239
|
+
query(spec.query_type)
|
240
|
+
instrument(:field, MultiplyInstrumenter.new(3))
|
241
|
+
instrument(:query, spec.variable_counter)
|
242
|
+
end
|
243
|
+
}
|
244
|
+
|
245
|
+
it "can modify field definitions" do
|
246
|
+
res = schema.execute(" { int(value: 2) } ")
|
247
|
+
assert_equal 6, res["data"]["int"]
|
248
|
+
end
|
249
|
+
|
250
|
+
it "can wrap query execution" do
|
251
|
+
schema.execute("query getInt($val: Int = 5){ int(value: $val) } ")
|
252
|
+
schema.execute("query getInt($val: Int = 5, $val2: Int = 3){ int(value: $val) int2: int(value: $val2) } ")
|
253
|
+
assert_equal [1, 2], variable_counter.counts
|
254
|
+
end
|
255
|
+
end
|
127
256
|
end
|
@@ -21,7 +21,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
21
21
|
|
22
22
|
it "finds undefined or missing-required arguments to fields and directives" do
|
23
23
|
# `wacky` above is handled by ArgumentsAreDefined, so only 6 are tested below
|
24
|
-
assert_equal(
|
24
|
+
assert_equal(8, errors.length)
|
25
25
|
|
26
26
|
query_root_error = {
|
27
27
|
"message"=>"Argument 'id' on Field 'stringCheese' has an invalid value. Expected type 'Int!'.",
|
@@ -8,7 +8,7 @@ class TypeCheckValidator
|
|
8
8
|
def validate(context)
|
9
9
|
self.class.checks.clear
|
10
10
|
context.visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) {
|
11
|
-
self.class.checks << context.object_types.map
|
11
|
+
self.class.checks << context.object_types.map {|t| t.name || t.kind.name }
|
12
12
|
}
|
13
13
|
end
|
14
14
|
end
|
@@ -29,8 +29,8 @@ describe GraphQL::StaticValidation::TypeStack do
|
|
29
29
|
validator.validate(query)
|
30
30
|
expected = [
|
31
31
|
["Query", "Cheese"],
|
32
|
-
["Query", "Cheese", "
|
33
|
-
["Edible", "
|
32
|
+
["Query", "Cheese", "NON_NULL"],
|
33
|
+
["Edible", "NON_NULL"]
|
34
34
|
]
|
35
35
|
assert_equal(expected, TypeCheckValidator.checks)
|
36
36
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -16,7 +16,33 @@ Minitest::Spec.make_my_diffs_pretty!
|
|
16
16
|
# to be shown.
|
17
17
|
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
18
18
|
|
19
|
-
|
19
|
+
|
20
|
+
# This is for convenient access to metadata in test definitions
|
21
|
+
assign_metadata_key = -> (target, key, value) { target.metadata[key] = value }
|
22
|
+
GraphQL::BaseType.accepts_definitions(metadata: assign_metadata_key)
|
23
|
+
GraphQL::Field.accepts_definitions(metadata: assign_metadata_key)
|
24
|
+
GraphQL::Argument.accepts_definitions(metadata: assign_metadata_key)
|
25
|
+
GraphQL::EnumType::EnumValue.accepts_definitions(metadata: assign_metadata_key)
|
26
|
+
|
27
|
+
# Can be used as a GraphQL::Schema::Warden for some purposes, but allows anything
|
28
|
+
module PermissiveWarden
|
29
|
+
def self.input_fields(input_obj)
|
30
|
+
input_obj.arguments.values
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.enum_values(enum_type)
|
34
|
+
enum_type.values.values
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Can be used as a GraphQL::Schema::Warden for some purposes, but allows nothing
|
39
|
+
module NothingWarden
|
40
|
+
def self.enum_values(enum_type)
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Load support files
|
20
46
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
21
47
|
|
22
48
|
def star_wars_query(string, variables={})
|
data/spec/support/dairy_app.rb
CHANGED
@@ -33,10 +33,12 @@ end
|
|
33
33
|
DairyAnimalEnum = GraphQL::EnumType.define do
|
34
34
|
name "DairyAnimal"
|
35
35
|
description "An animal which can yield milk"
|
36
|
-
value("COW",
|
37
|
-
value("
|
38
|
-
value("
|
39
|
-
value("
|
36
|
+
value("COW", "Animal with black and white spots", value: 1)
|
37
|
+
value("DONKEY", "Animal with fur", value: :donkey)
|
38
|
+
value("GOAT", "Animal with horns")
|
39
|
+
value("REINDEER", "Animal with horns", value: 'reindeer')
|
40
|
+
value("SHEEP", "Animal with wool")
|
41
|
+
value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
|
40
42
|
end
|
41
43
|
|
42
44
|
CheeseType = GraphQL::ObjectType.define do
|
@@ -57,7 +59,8 @@ CheeseType = GraphQL::ObjectType.define do
|
|
57
59
|
field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
|
58
60
|
# metadata test
|
59
61
|
joins [:cheeses, :milks]
|
60
|
-
argument :source, !types[!DairyAnimalEnum]
|
62
|
+
argument :source, !types[!DairyAnimalEnum]
|
63
|
+
argument :nullableSource, types[!DairyAnimalEnum], default_value: [1]
|
61
64
|
resolve ->(t, a, c) {
|
62
65
|
# get the strings out:
|
63
66
|
sources = a["source"]
|
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: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -271,6 +271,12 @@ files:
|
|
271
271
|
- lib/graphql/argument.rb
|
272
272
|
- lib/graphql/base_type.rb
|
273
273
|
- lib/graphql/boolean_type.rb
|
274
|
+
- lib/graphql/compatibility.rb
|
275
|
+
- lib/graphql/compatibility/execution_specification.rb
|
276
|
+
- lib/graphql/compatibility/query_parser_specification.rb
|
277
|
+
- lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb
|
278
|
+
- lib/graphql/compatibility/query_parser_specification/query_assertions.rb
|
279
|
+
- lib/graphql/compatibility/schema_parser_specification.rb
|
274
280
|
- lib/graphql/define.rb
|
275
281
|
- lib/graphql/define/assign_argument.rb
|
276
282
|
- lib/graphql/define/assign_connection.rb
|
@@ -330,7 +336,6 @@ files:
|
|
330
336
|
- lib/graphql/language/nodes.rb
|
331
337
|
- lib/graphql/language/parser.rb
|
332
338
|
- lib/graphql/language/parser.y
|
333
|
-
- lib/graphql/language/parser_tests.rb
|
334
339
|
- lib/graphql/language/token.rb
|
335
340
|
- lib/graphql/language/visitor.rb
|
336
341
|
- lib/graphql/list_type.rb
|
@@ -365,7 +370,9 @@ files:
|
|
365
370
|
- lib/graphql/relay/relation_connection.rb
|
366
371
|
- lib/graphql/scalar_type.rb
|
367
372
|
- lib/graphql/schema.rb
|
373
|
+
- lib/graphql/schema/build_from_definition.rb
|
368
374
|
- lib/graphql/schema/catchall_middleware.rb
|
375
|
+
- lib/graphql/schema/instrumented_field_map.rb
|
369
376
|
- lib/graphql/schema/invalid_type_error.rb
|
370
377
|
- lib/graphql/schema/loader.rb
|
371
378
|
- lib/graphql/schema/middleware_chain.rb
|
@@ -378,6 +385,7 @@ files:
|
|
378
385
|
- lib/graphql/schema/type_map.rb
|
379
386
|
- lib/graphql/schema/unique_within_type.rb
|
380
387
|
- lib/graphql/schema/validation.rb
|
388
|
+
- lib/graphql/schema/warden.rb
|
381
389
|
- lib/graphql/static_validation.rb
|
382
390
|
- lib/graphql/static_validation/all_rules.rb
|
383
391
|
- lib/graphql/static_validation/arguments_validator.rb
|
@@ -421,6 +429,9 @@ files:
|
|
421
429
|
- spec/graphql/argument_spec.rb
|
422
430
|
- spec/graphql/base_type_spec.rb
|
423
431
|
- spec/graphql/boolean_type_spec.rb
|
432
|
+
- spec/graphql/compatibility/execution_specification_spec.rb
|
433
|
+
- spec/graphql/compatibility/query_parser_specification_spec.rb
|
434
|
+
- spec/graphql/compatibility/schema_parser_specification_spec.rb
|
424
435
|
- spec/graphql/define/instance_definable_spec.rb
|
425
436
|
- spec/graphql/directive_spec.rb
|
426
437
|
- spec/graphql/enum_type_spec.rb
|
@@ -464,6 +475,7 @@ files:
|
|
464
475
|
- spec/graphql/relay/page_info_spec.rb
|
465
476
|
- spec/graphql/relay/relation_connection_spec.rb
|
466
477
|
- spec/graphql/scalar_type_spec.rb
|
478
|
+
- spec/graphql/schema/build_from_definition_spec.rb
|
467
479
|
- spec/graphql/schema/catchall_middleware_spec.rb
|
468
480
|
- spec/graphql/schema/loader_spec.rb
|
469
481
|
- spec/graphql/schema/middleware_chain_spec.rb
|
@@ -474,6 +486,7 @@ files:
|
|
474
486
|
- spec/graphql/schema/type_expression_spec.rb
|
475
487
|
- spec/graphql/schema/unique_within_type_spec.rb
|
476
488
|
- spec/graphql/schema/validation_spec.rb
|
489
|
+
- spec/graphql/schema/warden_spec.rb
|
477
490
|
- spec/graphql/schema_spec.rb
|
478
491
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
479
492
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
@@ -540,6 +553,9 @@ test_files:
|
|
540
553
|
- spec/graphql/argument_spec.rb
|
541
554
|
- spec/graphql/base_type_spec.rb
|
542
555
|
- spec/graphql/boolean_type_spec.rb
|
556
|
+
- spec/graphql/compatibility/execution_specification_spec.rb
|
557
|
+
- spec/graphql/compatibility/query_parser_specification_spec.rb
|
558
|
+
- spec/graphql/compatibility/schema_parser_specification_spec.rb
|
543
559
|
- spec/graphql/define/instance_definable_spec.rb
|
544
560
|
- spec/graphql/directive_spec.rb
|
545
561
|
- spec/graphql/enum_type_spec.rb
|
@@ -583,6 +599,7 @@ test_files:
|
|
583
599
|
- spec/graphql/relay/page_info_spec.rb
|
584
600
|
- spec/graphql/relay/relation_connection_spec.rb
|
585
601
|
- spec/graphql/scalar_type_spec.rb
|
602
|
+
- spec/graphql/schema/build_from_definition_spec.rb
|
586
603
|
- spec/graphql/schema/catchall_middleware_spec.rb
|
587
604
|
- spec/graphql/schema/loader_spec.rb
|
588
605
|
- spec/graphql/schema/middleware_chain_spec.rb
|
@@ -593,6 +610,7 @@ test_files:
|
|
593
610
|
- spec/graphql/schema/type_expression_spec.rb
|
594
611
|
- spec/graphql/schema/unique_within_type_spec.rb
|
595
612
|
- spec/graphql/schema/validation_spec.rb
|
613
|
+
- spec/graphql/schema/warden_spec.rb
|
596
614
|
- spec/graphql/schema_spec.rb
|
597
615
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
598
616
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
@@ -1,809 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Language
|
3
|
-
# If you create your own GraphQL parser, can verify it using these tests.
|
4
|
-
#
|
5
|
-
# @example Include these tests in a Minitest suite
|
6
|
-
# require 'graphql/language/parser_tests'
|
7
|
-
#
|
8
|
-
# describe MyParser do
|
9
|
-
# include GraphQL::Language::ParserTests
|
10
|
-
# subject { MyParser }
|
11
|
-
# end
|
12
|
-
module ParserTests
|
13
|
-
def self.included(test)
|
14
|
-
test.send(:describe, "Parser Tests") do
|
15
|
-
let(:document) { subject.parse(query_string) }
|
16
|
-
let(:query_string) {%|
|
17
|
-
query getStuff($someVar: Int = 1, $anotherVar: [String!] ) @skip(if: false) {
|
18
|
-
myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
|
19
|
-
|
20
|
-
anotherField(someArg: [1,2,3]) {
|
21
|
-
nestedField
|
22
|
-
... moreNestedFields @skip(if: true)
|
23
|
-
}
|
24
|
-
|
25
|
-
... on OtherType @include(unless: false){
|
26
|
-
field(arg: [{key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER}])
|
27
|
-
anotherField
|
28
|
-
}
|
29
|
-
|
30
|
-
... {
|
31
|
-
id
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
|
-
fragment moreNestedFields on NestedType @or(something: "ok") {
|
36
|
-
anotherNestedField @enum(directive: true)
|
37
|
-
}
|
38
|
-
|}
|
39
|
-
|
40
|
-
describe ".parse" do
|
41
|
-
it "parses queries" do
|
42
|
-
assert document
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "visited nodes" do
|
46
|
-
let(:query) { document.definitions.first }
|
47
|
-
let(:fragment_def) { document.definitions.last }
|
48
|
-
|
49
|
-
it "creates a valid document" do
|
50
|
-
assert document.is_a?(GraphQL::Language::Nodes::Document)
|
51
|
-
assert_equal 2, document.definitions.length
|
52
|
-
end
|
53
|
-
|
54
|
-
it "creates a valid operation" do
|
55
|
-
assert query.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
56
|
-
assert_equal "getStuff", query.name
|
57
|
-
assert_equal "query", query.operation_type
|
58
|
-
assert_equal 2, query.variables.length
|
59
|
-
assert_equal 4, query.selections.length
|
60
|
-
assert_equal 1, query.directives.length
|
61
|
-
assert_equal [2, 13], [query.line, query.col]
|
62
|
-
end
|
63
|
-
|
64
|
-
it "creates a valid fragment definition" do
|
65
|
-
assert fragment_def.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
66
|
-
assert_equal "moreNestedFields", fragment_def.name
|
67
|
-
assert_equal 1, fragment_def.selections.length
|
68
|
-
assert_equal "NestedType", fragment_def.type.name
|
69
|
-
assert_equal 1, fragment_def.directives.length
|
70
|
-
assert_equal [20, 13], fragment_def.position
|
71
|
-
end
|
72
|
-
|
73
|
-
describe "variable definitions" do
|
74
|
-
let(:optional_var) { query.variables.first }
|
75
|
-
it "gets name and type" do
|
76
|
-
assert_equal "someVar", optional_var.name
|
77
|
-
assert_equal "Int", optional_var.type.name
|
78
|
-
end
|
79
|
-
|
80
|
-
it "gets default value" do
|
81
|
-
assert_equal 1, optional_var.default_value
|
82
|
-
end
|
83
|
-
|
84
|
-
it "gets position info" do
|
85
|
-
assert_equal [2, 28], optional_var.position
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "fields" do
|
90
|
-
let(:leaf_field) { query.selections.first }
|
91
|
-
let(:parent_field) { query.selections[1] }
|
92
|
-
|
93
|
-
it "gets name, alias, arguments and directives" do
|
94
|
-
assert_equal "someField", leaf_field.name
|
95
|
-
assert_equal "myField", leaf_field.alias
|
96
|
-
assert_equal 2, leaf_field.directives.length
|
97
|
-
assert_equal 2, leaf_field.arguments.length
|
98
|
-
end
|
99
|
-
|
100
|
-
it "gets nested fields" do
|
101
|
-
assert_equal 2, parent_field.selections.length
|
102
|
-
end
|
103
|
-
|
104
|
-
it "gets location info" do
|
105
|
-
assert_equal [3 ,15], leaf_field.position
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "when the arguments list is empty" do
|
109
|
-
let(:query_string) { "{ field() }"}
|
110
|
-
let(:field) { query.selections.first }
|
111
|
-
it "has zero arguments" do
|
112
|
-
assert_equal 0, field.arguments.length
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "when selections are empty" do
|
117
|
-
let(:query_string) { "{ field { } }"}
|
118
|
-
let(:field) { query.selections.first }
|
119
|
-
it "has zero selections" do
|
120
|
-
assert_equal 0, field.selections.length
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
describe "arguments" do
|
126
|
-
let(:literal_argument) { query.selections.first.arguments.last }
|
127
|
-
let(:variable_argument) { query.selections.first.arguments.first }
|
128
|
-
|
129
|
-
it "gets name and literal value" do
|
130
|
-
assert_equal "ok", literal_argument.name
|
131
|
-
assert_equal 1.4, literal_argument.value
|
132
|
-
end
|
133
|
-
|
134
|
-
it "gets name and variable value" do
|
135
|
-
assert_equal "someArg", variable_argument.name
|
136
|
-
assert_equal "someVar", variable_argument.value.name
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
it "gets position info" do
|
141
|
-
assert_equal [3, 34], variable_argument.position
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
describe "fragment spreads" do
|
146
|
-
let(:fragment_spread) { query.selections[1].selections.last }
|
147
|
-
it "gets the name and directives" do
|
148
|
-
assert_equal "moreNestedFields", fragment_spread.name
|
149
|
-
assert_equal 1, fragment_spread.directives.length
|
150
|
-
end
|
151
|
-
|
152
|
-
it "gets position info" do
|
153
|
-
assert_equal [7, 17], fragment_spread.position
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
describe "directives" do
|
158
|
-
let(:variable_directive) { query.selections.first.directives.first }
|
159
|
-
|
160
|
-
it "gets the name and arguments" do
|
161
|
-
assert_equal "skip", variable_directive.name
|
162
|
-
assert_equal "if", variable_directive.arguments.first.name
|
163
|
-
assert_equal 1, variable_directive.arguments.length
|
164
|
-
end
|
165
|
-
|
166
|
-
it "gets position info" do
|
167
|
-
assert_equal [3, 62], variable_directive.position
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
describe "inline fragments" do
|
172
|
-
let(:inline_fragment) { query.selections[2] }
|
173
|
-
let(:typeless_inline_fragment) { query.selections[3] }
|
174
|
-
|
175
|
-
it "gets the type and directives" do
|
176
|
-
assert_equal "OtherType", inline_fragment.type.name
|
177
|
-
assert_equal 2, inline_fragment.selections.length
|
178
|
-
assert_equal 1, inline_fragment.directives.length
|
179
|
-
end
|
180
|
-
|
181
|
-
it "gets inline fragments without type conditions" do
|
182
|
-
assert_equal nil, typeless_inline_fragment.type
|
183
|
-
assert_equal 1, typeless_inline_fragment.selections.length
|
184
|
-
assert_equal 0, typeless_inline_fragment.directives.length
|
185
|
-
end
|
186
|
-
|
187
|
-
it "gets position info" do
|
188
|
-
assert_equal [10, 15], inline_fragment.position
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
describe "inputs" do
|
193
|
-
let(:query_string) {%|
|
194
|
-
{
|
195
|
-
field(
|
196
|
-
int: 3,
|
197
|
-
float: 4.7e-24,
|
198
|
-
bool: false,
|
199
|
-
string: "☀︎🏆\\n escaped \\" unicode \\u00b6 /",
|
200
|
-
enum: ENUM_NAME,
|
201
|
-
array: [7, 8, 9]
|
202
|
-
object: {a: [1,2,3], b: {c: "4"}}
|
203
|
-
unicode_bom: "\xef\xbb\xbfquery"
|
204
|
-
keywordEnum: on
|
205
|
-
)
|
206
|
-
}
|
207
|
-
|}
|
208
|
-
|
209
|
-
let(:inputs) { document.definitions.first.selections.first.arguments }
|
210
|
-
|
211
|
-
it "parses ints" do
|
212
|
-
assert_equal 3, inputs[0].value
|
213
|
-
end
|
214
|
-
|
215
|
-
it "parses floats" do
|
216
|
-
assert_equal 0.47e-23, inputs[1].value
|
217
|
-
end
|
218
|
-
|
219
|
-
it "parses booleans" do
|
220
|
-
assert_equal false, inputs[2].value
|
221
|
-
end
|
222
|
-
|
223
|
-
it "parses UTF-8 strings" do
|
224
|
-
assert_equal %|☀︎🏆\n escaped " unicode ¶ /|, inputs[3].value
|
225
|
-
end
|
226
|
-
|
227
|
-
it "parses enums" do
|
228
|
-
assert_instance_of GraphQL::Language::Nodes::Enum, inputs[4].value
|
229
|
-
assert_equal "ENUM_NAME", inputs[4].value.name
|
230
|
-
end
|
231
|
-
|
232
|
-
it "parses arrays" do
|
233
|
-
assert_equal [7,8,9], inputs[5].value
|
234
|
-
end
|
235
|
-
|
236
|
-
it "parses objects" do
|
237
|
-
obj = inputs[6].value
|
238
|
-
assert_equal "a", obj.arguments[0].name
|
239
|
-
assert_equal [1,2,3], obj.arguments[0].value
|
240
|
-
assert_equal "b", obj.arguments[1].name
|
241
|
-
assert_equal "c", obj.arguments[1].value.arguments[0].name
|
242
|
-
assert_equal "4", obj.arguments[1].value.arguments[0].value
|
243
|
-
end
|
244
|
-
|
245
|
-
it "parses unicode bom" do
|
246
|
-
obj = inputs[7].value
|
247
|
-
assert_equal %|\xef\xbb\xbfquery|, inputs[7].value
|
248
|
-
end
|
249
|
-
|
250
|
-
it "parses enum 'on''" do
|
251
|
-
assert_equal "on", inputs[8].value.name
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
describe "unnamed queries" do
|
257
|
-
let(:query_string) {%|
|
258
|
-
{ name, age, height }
|
259
|
-
|}
|
260
|
-
let(:operation) { document.definitions.first }
|
261
|
-
|
262
|
-
it "parses unnamed queries" do
|
263
|
-
assert_equal 1, document.definitions.length
|
264
|
-
assert_equal "query", operation.operation_type
|
265
|
-
assert_equal nil, operation.name
|
266
|
-
assert_equal 3, operation.selections.length
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
describe "introspection query" do
|
271
|
-
let(:query_string) { GraphQL::Introspection::INTROSPECTION_QUERY }
|
272
|
-
|
273
|
-
it "parses a big ol' query" do
|
274
|
-
assert(document)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
describe "schema with comments" do
|
279
|
-
let(:query_string) {%|
|
280
|
-
# Schema at beginning of file
|
281
|
-
|
282
|
-
schema {
|
283
|
-
query: Hello
|
284
|
-
}
|
285
|
-
|
286
|
-
# Comment between two definitions are omitted
|
287
|
-
|
288
|
-
# This is a directive
|
289
|
-
directive @foo(
|
290
|
-
# It has an argument
|
291
|
-
arg: Int
|
292
|
-
) on FIELD
|
293
|
-
|
294
|
-
# Multiline comment
|
295
|
-
#
|
296
|
-
# With an enum
|
297
|
-
enum Color {
|
298
|
-
RED
|
299
|
-
|
300
|
-
# Not a creative color
|
301
|
-
GREEN
|
302
|
-
BLUE
|
303
|
-
}
|
304
|
-
|
305
|
-
#Comment without preceding space
|
306
|
-
type Hello {
|
307
|
-
# And a field to boot
|
308
|
-
str: String
|
309
|
-
}
|
310
|
-
|
311
|
-
# Comment for input object types
|
312
|
-
input Car {
|
313
|
-
# Color of the car
|
314
|
-
color: String!
|
315
|
-
}
|
316
|
-
|
317
|
-
# Comment for interface definitions
|
318
|
-
interface Vehicle {
|
319
|
-
# Amount of wheels
|
320
|
-
wheels: Int!
|
321
|
-
}
|
322
|
-
|
323
|
-
# Comment at the end of schema
|
324
|
-
|}
|
325
|
-
|
326
|
-
it "parses successfully" do
|
327
|
-
document = subject.parse(query_string)
|
328
|
-
|
329
|
-
assert_equal 6, document.definitions.size
|
330
|
-
|
331
|
-
schema_definition = document.definitions.shift
|
332
|
-
assert_equal GraphQL::Language::Nodes::SchemaDefinition, schema_definition.class
|
333
|
-
|
334
|
-
directive_definition = document.definitions.shift
|
335
|
-
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, directive_definition.class
|
336
|
-
assert_equal 'This is a directive', directive_definition.description
|
337
|
-
|
338
|
-
enum_type_definition = document.definitions.shift
|
339
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, enum_type_definition.class
|
340
|
-
assert_equal "Multiline comment\n\nWith an enum", enum_type_definition.description
|
341
|
-
|
342
|
-
assert_nil enum_type_definition.values[0].description
|
343
|
-
assert_equal 'Not a creative color', enum_type_definition.values[1].description
|
344
|
-
|
345
|
-
object_type_definition = document.definitions.shift
|
346
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, object_type_definition.class
|
347
|
-
assert_equal 'Comment without preceding space', object_type_definition.description
|
348
|
-
assert_equal 'And a field to boot', object_type_definition.fields[0].description
|
349
|
-
|
350
|
-
input_object_type_definition = document.definitions.shift
|
351
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, input_object_type_definition.class
|
352
|
-
assert_equal 'Comment for input object types', input_object_type_definition.description
|
353
|
-
assert_equal 'Color of the car', input_object_type_definition.fields[0].description
|
354
|
-
|
355
|
-
interface_type_definition = document.definitions.shift
|
356
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, interface_type_definition.class
|
357
|
-
assert_equal 'Comment for interface definitions', interface_type_definition.description
|
358
|
-
assert_equal 'Amount of wheels', interface_type_definition.fields[0].description
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
describe "schema" do
|
363
|
-
it "parses the test schema" do
|
364
|
-
schema = DummySchema
|
365
|
-
schema_string = GraphQL::Schema::Printer.print_schema(schema)
|
366
|
-
|
367
|
-
document = subject.parse(schema_string)
|
368
|
-
|
369
|
-
assert_equal schema_string, document.to_query_string
|
370
|
-
end
|
371
|
-
|
372
|
-
it "parses mimal schema definition" do
|
373
|
-
document = subject.parse('schema { query: QueryRoot }')
|
374
|
-
|
375
|
-
schema = document.definitions.first
|
376
|
-
assert_equal 'QueryRoot', schema.query
|
377
|
-
assert_equal nil, schema.mutation
|
378
|
-
assert_equal nil, schema.subscription
|
379
|
-
end
|
380
|
-
|
381
|
-
it "parses full schema definitions" do
|
382
|
-
document = subject.parse('
|
383
|
-
schema {
|
384
|
-
query: QueryRoot
|
385
|
-
mutation: MutationRoot
|
386
|
-
subscription: SubscriptionRoot
|
387
|
-
}
|
388
|
-
')
|
389
|
-
|
390
|
-
schema = document.definitions.first
|
391
|
-
assert_equal 'QueryRoot', schema.query
|
392
|
-
assert_equal 'MutationRoot', schema.mutation
|
393
|
-
assert_equal 'SubscriptionRoot', schema.subscription
|
394
|
-
end
|
395
|
-
|
396
|
-
it "parses object types" do
|
397
|
-
document = subject.parse('
|
398
|
-
type Comment implements Node {
|
399
|
-
id: ID!
|
400
|
-
}
|
401
|
-
')
|
402
|
-
|
403
|
-
type = document.definitions.first
|
404
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
405
|
-
assert_equal 'Comment', type.name
|
406
|
-
assert_equal ['Node'], type.interfaces.map(&:name)
|
407
|
-
assert_equal ['id'], type.fields.map(&:name)
|
408
|
-
assert_equal [], type.fields[0].arguments
|
409
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
410
|
-
end
|
411
|
-
|
412
|
-
it "parses object types with directives" do
|
413
|
-
document = subject.parse('
|
414
|
-
type Comment implements Node @deprecated(reason: "No longer supported") {
|
415
|
-
id: ID!
|
416
|
-
}
|
417
|
-
')
|
418
|
-
|
419
|
-
type = document.definitions.first
|
420
|
-
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
421
|
-
assert_equal 'Comment', type.name
|
422
|
-
assert_equal ['Node'], type.interfaces.map(&:name)
|
423
|
-
assert_equal ['id'], type.fields.map(&:name)
|
424
|
-
assert_equal [], type.fields[0].arguments
|
425
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
426
|
-
assert_equal 1, type.directives.length
|
427
|
-
|
428
|
-
deprecated_directive = type.directives[0]
|
429
|
-
assert_equal 'deprecated', deprecated_directive.name
|
430
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
431
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
432
|
-
end
|
433
|
-
|
434
|
-
it "parses field arguments" do
|
435
|
-
document = subject.parse('
|
436
|
-
type Mutation {
|
437
|
-
post(id: ID!, data: PostData = { message: "First!1!", type: BLOG, tags: ["Test", "Annoying"] }): Post
|
438
|
-
}
|
439
|
-
')
|
440
|
-
|
441
|
-
field = document.definitions.first.fields.first
|
442
|
-
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
443
|
-
data_arg = field.arguments[1]
|
444
|
-
assert_equal 'PostData', data_arg.type.name
|
445
|
-
assert_equal ['message', 'type', 'tags'], data_arg.default_value.arguments.map(&:name)
|
446
|
-
tags_arg = data_arg.default_value.arguments[2]
|
447
|
-
assert_equal ['Test', 'Annoying'], tags_arg.value
|
448
|
-
end
|
449
|
-
|
450
|
-
it "parses field arguments with directives" do
|
451
|
-
document = subject.parse('
|
452
|
-
type Mutation {
|
453
|
-
post(id: ID! @deprecated(reason: "No longer supported"), data: String): Post
|
454
|
-
}
|
455
|
-
')
|
456
|
-
|
457
|
-
field = document.definitions.first.fields.first
|
458
|
-
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
459
|
-
id_arg = field.arguments[0]
|
460
|
-
|
461
|
-
deprecated_directive = id_arg.directives[0]
|
462
|
-
assert_equal 'deprecated', deprecated_directive.name
|
463
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
464
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
465
|
-
end
|
466
|
-
|
467
|
-
it "parses directive definition" do
|
468
|
-
document = subject.parse('
|
469
|
-
directive @include(if: Boolean!)
|
470
|
-
on FIELD
|
471
|
-
| FRAGMENT_SPREAD
|
472
|
-
| INLINE_FRAGMENT
|
473
|
-
')
|
474
|
-
|
475
|
-
type = document.definitions.first
|
476
|
-
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, type.class
|
477
|
-
assert_equal 'include', type.name
|
478
|
-
|
479
|
-
assert_equal 1, type.arguments.length
|
480
|
-
assert_equal 'if', type.arguments[0].name
|
481
|
-
assert_equal 'Boolean', type.arguments[0].type.of_type.name
|
482
|
-
|
483
|
-
assert_equal ['FIELD', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT'], type.locations
|
484
|
-
end
|
485
|
-
|
486
|
-
it "parses scalar types" do
|
487
|
-
document = subject.parse('scalar DateTime')
|
488
|
-
|
489
|
-
type = document.definitions.first
|
490
|
-
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
491
|
-
assert_equal 'DateTime', type.name
|
492
|
-
end
|
493
|
-
|
494
|
-
it "parses scalar types with directives" do
|
495
|
-
document = subject.parse('scalar DateTime @deprecated(reason: "No longer supported")')
|
496
|
-
|
497
|
-
type = document.definitions.first
|
498
|
-
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
499
|
-
assert_equal 'DateTime', type.name
|
500
|
-
assert_equal 1, type.directives.length
|
501
|
-
|
502
|
-
deprecated_directive = type.directives[0]
|
503
|
-
assert_equal 'deprecated', deprecated_directive.name
|
504
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
505
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
506
|
-
end
|
507
|
-
|
508
|
-
it "parses interface types" do
|
509
|
-
document = subject.parse('
|
510
|
-
interface Node {
|
511
|
-
id: ID!
|
512
|
-
}
|
513
|
-
')
|
514
|
-
|
515
|
-
type = document.definitions.first
|
516
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
|
517
|
-
assert_equal 'Node', type.name
|
518
|
-
assert_equal ['id'], type.fields.map(&:name)
|
519
|
-
assert_equal [], type.fields[0].arguments
|
520
|
-
assert_equal 'ID', type.fields[0].type.of_type.name
|
521
|
-
end
|
522
|
-
|
523
|
-
it "parses interface types with directives" do
|
524
|
-
document = subject.parse('
|
525
|
-
interface Node @deprecated(reason: "No longer supported") {
|
526
|
-
id: ID!
|
527
|
-
}
|
528
|
-
')
|
529
|
-
|
530
|
-
type = document.definitions.first
|
531
|
-
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
|
532
|
-
assert_equal 'Node', type.name
|
533
|
-
assert_equal 1, type.directives.length
|
534
|
-
|
535
|
-
deprecated_directive = type.directives[0]
|
536
|
-
assert_equal 'deprecated', deprecated_directive.name
|
537
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
538
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
539
|
-
end
|
540
|
-
|
541
|
-
it "parses enum types" do
|
542
|
-
document = subject.parse('
|
543
|
-
enum DogCommand {
|
544
|
-
SIT
|
545
|
-
DOWN @deprecated(reason: "No longer supported")
|
546
|
-
HEEL
|
547
|
-
}
|
548
|
-
')
|
549
|
-
|
550
|
-
type = document.definitions.first
|
551
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
552
|
-
assert_equal 'DogCommand', type.name
|
553
|
-
assert_equal 3, type.values.length
|
554
|
-
|
555
|
-
assert_equal 'SIT', type.values[0].name
|
556
|
-
assert_equal [], type.values[0].directives
|
557
|
-
|
558
|
-
assert_equal 'DOWN', type.values[1].name
|
559
|
-
assert_equal 1, type.values[1].directives.length
|
560
|
-
deprecated_directive = type.values[1].directives[0]
|
561
|
-
assert_equal 'deprecated', deprecated_directive.name
|
562
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
563
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
564
|
-
|
565
|
-
assert_equal 'HEEL', type.values[2].name
|
566
|
-
assert_equal [], type.values[2].directives
|
567
|
-
end
|
568
|
-
|
569
|
-
it "parses enum types with directives" do
|
570
|
-
document = subject.parse('
|
571
|
-
enum DogCommand @deprecated(reason: "No longer supported") {
|
572
|
-
SIT
|
573
|
-
}
|
574
|
-
')
|
575
|
-
|
576
|
-
type = document.definitions.first
|
577
|
-
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
578
|
-
assert_equal 'DogCommand', type.name
|
579
|
-
assert_equal 1, type.directives.length
|
580
|
-
|
581
|
-
deprecated_directive = type.directives[0]
|
582
|
-
assert_equal 'deprecated', deprecated_directive.name
|
583
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
584
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
585
|
-
end
|
586
|
-
|
587
|
-
it "parses input object types" do
|
588
|
-
document = subject.parse('
|
589
|
-
input EmptyMutationInput {
|
590
|
-
clientMutationId: String
|
591
|
-
}
|
592
|
-
')
|
593
|
-
|
594
|
-
type = document.definitions.first
|
595
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
596
|
-
assert_equal 'EmptyMutationInput', type.name
|
597
|
-
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
598
|
-
assert_equal 'String', type.fields[0].type.name
|
599
|
-
assert_equal nil, type.fields[0].default_value
|
600
|
-
end
|
601
|
-
|
602
|
-
it "parses input object types with directives" do
|
603
|
-
document = subject.parse('
|
604
|
-
input EmptyMutationInput @deprecated(reason: "No longer supported") {
|
605
|
-
clientMutationId: String
|
606
|
-
}
|
607
|
-
')
|
608
|
-
|
609
|
-
type = document.definitions.first
|
610
|
-
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
611
|
-
assert_equal 'EmptyMutationInput', type.name
|
612
|
-
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
613
|
-
assert_equal 'String', type.fields[0].type.name
|
614
|
-
assert_equal nil, type.fields[0].default_value
|
615
|
-
assert_equal 1, type.directives.length
|
616
|
-
|
617
|
-
deprecated_directive = type.directives[0]
|
618
|
-
assert_equal 'deprecated', deprecated_directive.name
|
619
|
-
assert_equal 'reason', deprecated_directive.arguments[0].name
|
620
|
-
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
621
|
-
|
622
|
-
end
|
623
|
-
end
|
624
|
-
end
|
625
|
-
|
626
|
-
describe "errors" do
|
627
|
-
let(:query_string) {%| query doSomething { bogus { } |}
|
628
|
-
it "raises a parse error" do
|
629
|
-
err = assert_raises(GraphQL::ParseError) { document }
|
630
|
-
end
|
631
|
-
|
632
|
-
it "correctly identifies parse error location and content" do
|
633
|
-
e = assert_raises(GraphQL::ParseError) do
|
634
|
-
GraphQL.parse("
|
635
|
-
query getCoupons {
|
636
|
-
allCoupons: {data{id}}
|
637
|
-
}
|
638
|
-
")
|
639
|
-
end
|
640
|
-
assert_includes(e.message, '"{"')
|
641
|
-
assert_includes(e.message, "LCURLY")
|
642
|
-
assert_equal(3, e.line)
|
643
|
-
assert_equal(33, e.col)
|
644
|
-
end
|
645
|
-
|
646
|
-
it "handles unexpected ends" do
|
647
|
-
err = assert_raises(GraphQL::ParseError) { GraphQL.parse("{ ") }
|
648
|
-
assert_equal "Unexpected end of document", err.message
|
649
|
-
end
|
650
|
-
|
651
|
-
it "rejects unsupported characters" do
|
652
|
-
e = assert_raises(GraphQL::ParseError) do
|
653
|
-
GraphQL.parse("{ field; }")
|
654
|
-
end
|
655
|
-
|
656
|
-
assert_includes(e.message, "Parse error on \";\"")
|
657
|
-
end
|
658
|
-
|
659
|
-
it "rejects control characters" do
|
660
|
-
e = assert_raises(GraphQL::ParseError) do
|
661
|
-
GraphQL.parse("{ \afield }")
|
662
|
-
end
|
663
|
-
|
664
|
-
assert_includes(e.message, "Parse error on \"\\a\"")
|
665
|
-
end
|
666
|
-
|
667
|
-
it "rejects partial BOM" do
|
668
|
-
e = assert_raises(GraphQL::ParseError) do
|
669
|
-
GraphQL.parse("{ \xeffield }")
|
670
|
-
end
|
671
|
-
|
672
|
-
assert_includes(e.message, "Parse error on \"\\xEF\"")
|
673
|
-
end
|
674
|
-
|
675
|
-
it "rejects vertical tabs" do
|
676
|
-
e = assert_raises(GraphQL::ParseError) do
|
677
|
-
GraphQL.parse("{ \vfield }")
|
678
|
-
end
|
679
|
-
|
680
|
-
assert_includes(e.message, "Parse error on \"\\v\"")
|
681
|
-
end
|
682
|
-
|
683
|
-
it "rejects form feed" do
|
684
|
-
e = assert_raises(GraphQL::ParseError) do
|
685
|
-
GraphQL.parse("{ \ffield }")
|
686
|
-
end
|
687
|
-
|
688
|
-
assert_includes(e.message, "Parse error on \"\\f\"")
|
689
|
-
end
|
690
|
-
|
691
|
-
it "rejects no break space" do
|
692
|
-
e = assert_raises(GraphQL::ParseError) do
|
693
|
-
GraphQL.parse("{ \xa0field }")
|
694
|
-
end
|
695
|
-
|
696
|
-
assert_includes(e.message, "Parse error on \"\\xA0\"")
|
697
|
-
end
|
698
|
-
|
699
|
-
it "rejects unterminated strings" do
|
700
|
-
e = assert_raises(GraphQL::ParseError) do
|
701
|
-
GraphQL.parse("\"")
|
702
|
-
end
|
703
|
-
|
704
|
-
assert_includes(e.message, "Parse error on \"\\\"\"")
|
705
|
-
|
706
|
-
e = assert_raises(GraphQL::ParseError) do
|
707
|
-
GraphQL.parse("\"\n\"")
|
708
|
-
end
|
709
|
-
|
710
|
-
assert_includes(e.message, "Parse error on \"\\n\"")
|
711
|
-
end
|
712
|
-
|
713
|
-
it "rejects bad escape sequence in strings" do
|
714
|
-
e = assert_raises(GraphQL::ParseError) do
|
715
|
-
GraphQL.parse("{ field(arg:\"\\x\") }")
|
716
|
-
end
|
717
|
-
|
718
|
-
assert_includes(e.message, "Parse error on bad Unicode escape sequence")
|
719
|
-
end
|
720
|
-
|
721
|
-
it "rejects incomplete escape sequence in strings" do
|
722
|
-
e = assert_raises(GraphQL::ParseError) do
|
723
|
-
GraphQL.parse("{ field(arg:\"\\u1\") }")
|
724
|
-
end
|
725
|
-
|
726
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
727
|
-
end
|
728
|
-
|
729
|
-
it "rejects unicode escape with bad chars" do
|
730
|
-
e = assert_raises(GraphQL::ParseError) do
|
731
|
-
GraphQL.parse("{ field(arg:\"\\u0XX1\") }")
|
732
|
-
end
|
733
|
-
|
734
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
735
|
-
|
736
|
-
e = assert_raises(GraphQL::ParseError) do
|
737
|
-
GraphQL.parse("{ field(arg:\"\\uXXXX\") }")
|
738
|
-
end
|
739
|
-
|
740
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
741
|
-
|
742
|
-
|
743
|
-
e = assert_raises(GraphQL::ParseError) do
|
744
|
-
GraphQL.parse("{ field(arg:\"\\uFXXX\") }")
|
745
|
-
end
|
746
|
-
|
747
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
748
|
-
|
749
|
-
|
750
|
-
e = assert_raises(GraphQL::ParseError) do
|
751
|
-
GraphQL.parse("{ field(arg:\"\\uXXXF\") }")
|
752
|
-
end
|
753
|
-
|
754
|
-
assert_includes(e.message, "bad Unicode escape sequence")
|
755
|
-
end
|
756
|
-
|
757
|
-
it "rejects fragments named 'on'" do
|
758
|
-
e = assert_raises(GraphQL::ParseError) do
|
759
|
-
GraphQL.parse("fragment on on on { on }")
|
760
|
-
end
|
761
|
-
|
762
|
-
assert_includes(e.message, "Parse error on \"on\"")
|
763
|
-
end
|
764
|
-
|
765
|
-
it "rejects fragment spread of 'on'" do
|
766
|
-
e = assert_raises(GraphQL::ParseError) do
|
767
|
-
GraphQL.parse("{ ...on }")
|
768
|
-
end
|
769
|
-
|
770
|
-
assert_includes(e.message, "Parse error on \"}\"")
|
771
|
-
end
|
772
|
-
|
773
|
-
it "rejects null value" do
|
774
|
-
e = assert_raises(GraphQL::ParseError) do
|
775
|
-
GraphQL.parse("{ fieldWithNullableStringInput(input: null) }")
|
776
|
-
end
|
777
|
-
|
778
|
-
assert_includes(e.message, "Parse error on \"null\"")
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
|
783
|
-
describe "whitespace" do
|
784
|
-
describe "whitespace-only queries" do
|
785
|
-
let(:query_string) { " " }
|
786
|
-
it "doesn't blow up" do
|
787
|
-
assert_equal [], document.definitions
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
describe "empty string queries" do
|
792
|
-
let(:query_string) { "" }
|
793
|
-
it "doesn't blow up" do
|
794
|
-
assert_equal [], document.definitions
|
795
|
-
end
|
796
|
-
end
|
797
|
-
|
798
|
-
describe "using tabs as whitespace" do
|
799
|
-
let(:query_string) { "\t{\t\tid, \tname}"}
|
800
|
-
it "parses the query" do
|
801
|
-
assert_equal 1, document.definitions.length
|
802
|
-
end
|
803
|
-
end
|
804
|
-
end
|
805
|
-
end
|
806
|
-
end
|
807
|
-
end
|
808
|
-
end
|
809
|
-
end
|