graphql 0.18.14 → 0.18.15
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/query_complexity.rb +15 -6
- data/lib/graphql/analysis/query_depth.rb +11 -10
- data/lib/graphql/directive.rb +2 -6
- data/lib/graphql/directive/include_directive.rb +0 -4
- data/lib/graphql/directive/skip_directive.rb +0 -4
- data/lib/graphql/execution/directive_checks.rb +22 -13
- data/lib/graphql/execution_error.rb +7 -0
- data/lib/graphql/internal_representation/node.rb +20 -2
- data/lib/graphql/internal_representation/rewrite.rb +66 -20
- data/lib/graphql/language/generation.rb +22 -8
- data/lib/graphql/language/nodes.rb +48 -20
- data/lib/graphql/language/parser.rb +436 -423
- data/lib/graphql/language/parser.y +22 -19
- data/lib/graphql/language/parser_tests.rb +131 -2
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -0
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution/value_resolution.rb +4 -1
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/static_validation/message.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +10 -12
- data/spec/graphql/directive_spec.rb +139 -1
- data/spec/graphql/execution_error_spec.rb +63 -3
- data/spec/graphql/introspection/type_type_spec.rb +2 -0
- data/spec/graphql/language/generation_spec.rb +55 -7
- data/spec/graphql/query/executor_spec.rb +4 -2
- data/spec/graphql/schema/catchall_middleware_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +1 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -6
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
- data/spec/graphql/static_validation/validator_spec.rb +1 -1
- data/spec/support/dairy_app.rb +8 -0
- metadata +30 -2
@@ -146,9 +146,12 @@ rule
|
|
146
146
|
name { return [val[0].to_s]}
|
147
147
|
| name_list name { val[0] << val[1].to_s }
|
148
148
|
|
149
|
-
|
150
|
-
|
151
|
-
|
149
|
+
enum_value_definition:
|
150
|
+
enum_name directives_list_opt { return make_node(:EnumValueDefinition, name: val[0], directives: val[1]) }
|
151
|
+
|
152
|
+
enum_value_definitions:
|
153
|
+
enum_value_definition { return [val[0]] }
|
154
|
+
| enum_value_definitions enum_value_definition { return val[0] << val[1] }
|
152
155
|
|
153
156
|
arguments_opt:
|
154
157
|
/* none */ { return [] }
|
@@ -194,7 +197,7 @@ rule
|
|
194
197
|
object_value_field:
|
195
198
|
name COLON input_value { return make_node(:Argument, name: val[0], value: val[2], position_source: val[0])}
|
196
199
|
|
197
|
-
enum_value: enum_name { return make_node(:Enum, name: val[0], position_source: val[0])}
|
200
|
+
enum_value: enum_name { return make_node(:Enum, name: val[0], position_source: val[0]) }
|
198
201
|
|
199
202
|
directives_list_opt:
|
200
203
|
/* none */ { return [] }
|
@@ -265,11 +268,11 @@ rule
|
|
265
268
|
| enum_type_definition
|
266
269
|
| input_object_type_definition
|
267
270
|
|
268
|
-
scalar_type_definition: SCALAR name { return make_node(:ScalarTypeDefinition, name: val[1]) }
|
271
|
+
scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2]) }
|
269
272
|
|
270
273
|
object_type_definition:
|
271
|
-
TYPE name implements_opt RCURLY field_definition_list LCURLY {
|
272
|
-
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], fields: val[
|
274
|
+
TYPE name implements_opt directives_list_opt RCURLY field_definition_list LCURLY {
|
275
|
+
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5])
|
273
276
|
}
|
274
277
|
|
275
278
|
implements_opt:
|
@@ -277,8 +280,8 @@ rule
|
|
277
280
|
| IMPLEMENTS name_list { return val[1] }
|
278
281
|
|
279
282
|
input_value_definition:
|
280
|
-
name COLON type default_value_opt {
|
281
|
-
return make_node(:InputValueDefinition, name: val[0], type: val[2], default_value: val[3])
|
283
|
+
name COLON type default_value_opt directives_list_opt {
|
284
|
+
return make_node(:InputValueDefinition, name: val[0], type: val[2], default_value: val[3], directives: val[4])
|
282
285
|
}
|
283
286
|
|
284
287
|
input_value_definition_list:
|
@@ -290,8 +293,8 @@ rule
|
|
290
293
|
| RPAREN input_value_definition_list LPAREN { return val[1] }
|
291
294
|
|
292
295
|
field_definition:
|
293
|
-
name arguments_definitions_opt COLON type {
|
294
|
-
return make_node(:FieldDefinition, name: val[0], arguments: val[1], type: val[3])
|
296
|
+
name arguments_definitions_opt COLON type directives_list_opt {
|
297
|
+
return make_node(:FieldDefinition, name: val[0], arguments: val[1], type: val[3], directives: val[4])
|
295
298
|
}
|
296
299
|
|
297
300
|
field_definition_list:
|
@@ -299,8 +302,8 @@ rule
|
|
299
302
|
| field_definition_list field_definition { val[0] << val[1] }
|
300
303
|
|
301
304
|
interface_type_definition:
|
302
|
-
INTERFACE name RCURLY field_definition_list LCURLY {
|
303
|
-
return make_node(:InterfaceTypeDefinition, name: val[1], fields: val[
|
305
|
+
INTERFACE name directives_list_opt RCURLY field_definition_list LCURLY {
|
306
|
+
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4])
|
304
307
|
}
|
305
308
|
|
306
309
|
union_members:
|
@@ -308,18 +311,18 @@ rule
|
|
308
311
|
| union_members PIPE name { val[0] << val[2].to_s }
|
309
312
|
|
310
313
|
union_type_definition:
|
311
|
-
UNION name EQUALS union_members {
|
312
|
-
return make_node(:UnionTypeDefinition, name: val[1], types: val[
|
314
|
+
UNION name directives_list_opt EQUALS union_members {
|
315
|
+
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4])
|
313
316
|
}
|
314
317
|
|
315
318
|
enum_type_definition:
|
316
|
-
ENUM name RCURLY
|
317
|
-
return make_node(:EnumTypeDefinition, name: val[1], values: val[
|
319
|
+
ENUM name directives_list_opt RCURLY enum_value_definitions LCURLY {
|
320
|
+
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4])
|
318
321
|
}
|
319
322
|
|
320
323
|
input_object_type_definition:
|
321
|
-
INPUT name RCURLY input_value_definition_list LCURLY {
|
322
|
-
return make_node(:InputObjectTypeDefinition, name: val[1], fields: val[
|
324
|
+
INPUT name directives_list_opt RCURLY input_value_definition_list LCURLY {
|
325
|
+
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4])
|
323
326
|
}
|
324
327
|
end
|
325
328
|
|
@@ -279,6 +279,7 @@ module GraphQL
|
|
279
279
|
it "parses the test schema" do
|
280
280
|
schema = DummySchema
|
281
281
|
schema_string = GraphQL::Schema::Printer.print_schema(schema)
|
282
|
+
|
282
283
|
document = subject.parse(schema_string)
|
283
284
|
|
284
285
|
assert_equal schema_string, document.to_query_string
|
@@ -324,6 +325,28 @@ module GraphQL
|
|
324
325
|
assert_equal 'ID', type.fields[0].type.of_type.name
|
325
326
|
end
|
326
327
|
|
328
|
+
it "parses object types with directives" do
|
329
|
+
document = subject.parse('
|
330
|
+
type Comment implements Node @deprecated(reason: "No longer supported") {
|
331
|
+
id: ID!
|
332
|
+
}
|
333
|
+
')
|
334
|
+
|
335
|
+
type = document.definitions.first
|
336
|
+
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
337
|
+
assert_equal 'Comment', type.name
|
338
|
+
assert_equal ['Node'], type.interfaces
|
339
|
+
assert_equal ['id'], type.fields.map(&:name)
|
340
|
+
assert_equal [], type.fields[0].arguments
|
341
|
+
assert_equal 'ID', type.fields[0].type.of_type.name
|
342
|
+
assert_equal 1, type.directives.length
|
343
|
+
|
344
|
+
deprecated_directive = type.directives[0]
|
345
|
+
assert_equal 'deprecated', deprecated_directive.name
|
346
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
347
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
348
|
+
end
|
349
|
+
|
327
350
|
it "parses field arguments" do
|
328
351
|
document = subject.parse('
|
329
352
|
type Mutation {
|
@@ -340,6 +363,23 @@ module GraphQL
|
|
340
363
|
assert_equal ['Test', 'Annoying'], tags_arg.value
|
341
364
|
end
|
342
365
|
|
366
|
+
it "parses field arguments with directives" do
|
367
|
+
document = subject.parse('
|
368
|
+
type Mutation {
|
369
|
+
post(id: ID! @deprecated(reason: "No longer supported"), data: String): Post
|
370
|
+
}
|
371
|
+
')
|
372
|
+
|
373
|
+
field = document.definitions.first.fields.first
|
374
|
+
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
375
|
+
id_arg = field.arguments[0]
|
376
|
+
|
377
|
+
deprecated_directive = id_arg.directives[0]
|
378
|
+
assert_equal 'deprecated', deprecated_directive.name
|
379
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
380
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
381
|
+
end
|
382
|
+
|
343
383
|
it "parses scalar types" do
|
344
384
|
document = subject.parse('scalar DateTime')
|
345
385
|
|
@@ -348,6 +388,20 @@ module GraphQL
|
|
348
388
|
assert_equal 'DateTime', type.name
|
349
389
|
end
|
350
390
|
|
391
|
+
it "parses scalar types with directives" do
|
392
|
+
document = subject.parse('scalar DateTime @deprecated(reason: "No longer supported")')
|
393
|
+
|
394
|
+
type = document.definitions.first
|
395
|
+
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
396
|
+
assert_equal 'DateTime', type.name
|
397
|
+
assert_equal 1, type.directives.length
|
398
|
+
|
399
|
+
deprecated_directive = type.directives[0]
|
400
|
+
assert_equal 'deprecated', deprecated_directive.name
|
401
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
402
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
403
|
+
end
|
404
|
+
|
351
405
|
it "parses interface types" do
|
352
406
|
document = subject.parse('
|
353
407
|
interface Node {
|
@@ -363,15 +417,68 @@ module GraphQL
|
|
363
417
|
assert_equal 'ID', type.fields[0].type.of_type.name
|
364
418
|
end
|
365
419
|
|
420
|
+
it "parses interface types with directives" do
|
421
|
+
document = subject.parse('
|
422
|
+
interface Node @deprecated(reason: "No longer supported") {
|
423
|
+
id: ID!
|
424
|
+
}
|
425
|
+
')
|
426
|
+
|
427
|
+
type = document.definitions.first
|
428
|
+
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
|
429
|
+
assert_equal 'Node', type.name
|
430
|
+
assert_equal 1, type.directives.length
|
431
|
+
|
432
|
+
deprecated_directive = type.directives[0]
|
433
|
+
assert_equal 'deprecated', deprecated_directive.name
|
434
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
435
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
436
|
+
end
|
437
|
+
|
366
438
|
it "parses enum types" do
|
367
439
|
document = subject.parse('
|
368
|
-
enum DogCommand {
|
440
|
+
enum DogCommand {
|
441
|
+
SIT
|
442
|
+
DOWN @deprecated(reason: "No longer supported")
|
443
|
+
HEEL
|
444
|
+
}
|
445
|
+
')
|
446
|
+
|
447
|
+
type = document.definitions.first
|
448
|
+
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
449
|
+
assert_equal 'DogCommand', type.name
|
450
|
+
assert_equal 3, type.values.length
|
451
|
+
|
452
|
+
assert_equal 'SIT', type.values[0].name
|
453
|
+
assert_equal [], type.values[0].directives
|
454
|
+
|
455
|
+
assert_equal 'DOWN', type.values[1].name
|
456
|
+
assert_equal 1, type.values[1].directives.length
|
457
|
+
deprecated_directive = type.values[1].directives[0]
|
458
|
+
assert_equal 'deprecated', deprecated_directive.name
|
459
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
460
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
461
|
+
|
462
|
+
assert_equal 'HEEL', type.values[2].name
|
463
|
+
assert_equal [], type.values[2].directives
|
464
|
+
end
|
465
|
+
|
466
|
+
it "parses enum types with directives" do
|
467
|
+
document = subject.parse('
|
468
|
+
enum DogCommand @deprecated(reason: "No longer supported") {
|
469
|
+
SIT
|
470
|
+
}
|
369
471
|
')
|
370
472
|
|
371
473
|
type = document.definitions.first
|
372
474
|
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
373
475
|
assert_equal 'DogCommand', type.name
|
374
|
-
assert_equal
|
476
|
+
assert_equal 1, type.directives.length
|
477
|
+
|
478
|
+
deprecated_directive = type.directives[0]
|
479
|
+
assert_equal 'deprecated', deprecated_directive.name
|
480
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
481
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
375
482
|
end
|
376
483
|
|
377
484
|
it "parses input object types" do
|
@@ -388,6 +495,28 @@ module GraphQL
|
|
388
495
|
assert_equal 'String', type.fields[0].type.name
|
389
496
|
assert_equal nil, type.fields[0].default_value
|
390
497
|
end
|
498
|
+
|
499
|
+
it "parses input object types with directives" do
|
500
|
+
document = subject.parse('
|
501
|
+
input EmptyMutationInput @deprecated(reason: "No longer supported") {
|
502
|
+
clientMutationId: String
|
503
|
+
}
|
504
|
+
')
|
505
|
+
|
506
|
+
type = document.definitions.first
|
507
|
+
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
508
|
+
assert_equal 'EmptyMutationInput', type.name
|
509
|
+
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
510
|
+
assert_equal 'String', type.fields[0].type.name
|
511
|
+
assert_equal nil, type.fields[0].default_value
|
512
|
+
assert_equal 1, type.directives.length
|
513
|
+
|
514
|
+
deprecated_directive = type.directives[0]
|
515
|
+
assert_equal 'deprecated', deprecated_directive.name
|
516
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
517
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
518
|
+
|
519
|
+
end
|
391
520
|
end
|
392
521
|
end
|
393
522
|
|
@@ -13,7 +13,7 @@ module GraphQL
|
|
13
13
|
|
14
14
|
def result
|
15
15
|
irep_node.children.each_with_object({}) do |(name, irep_node), memo|
|
16
|
-
if
|
16
|
+
if irep_node.included? && applies_to_type?(irep_node, type)
|
17
17
|
field_result = execution_context.strategy.field_resolution.new(
|
18
18
|
irep_node,
|
19
19
|
type,
|
@@ -45,10 +45,13 @@ module GraphQL
|
|
45
45
|
def non_null_result
|
46
46
|
wrapped_type = field_type.of_type
|
47
47
|
strategy_class = get_strategy_for_kind(wrapped_type.kind)
|
48
|
-
value.map do |item|
|
48
|
+
result = value.each_with_index.map do |item, index|
|
49
|
+
irep_node.index = index
|
49
50
|
inner_strategy = strategy_class.new(item, wrapped_type, target, parent_type, irep_node, execution_context)
|
50
51
|
inner_strategy.result
|
51
52
|
end
|
53
|
+
irep_node.index = nil
|
54
|
+
result
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
@@ -94,7 +94,7 @@ module GraphQL
|
|
94
94
|
field_type = type.input_fields.fetch(field_name.to_s).type
|
95
95
|
"#{field_name}: #{print_value(field_value, field_type)}"
|
96
96
|
}.join(", ")
|
97
|
-
"{
|
97
|
+
"{#{fields}}"
|
98
98
|
when NonNullType
|
99
99
|
print_value(value, type.of_type)
|
100
100
|
when ListType
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# graphql <img src="https://cloud.githubusercontent.com/assets/2231765/9094460/cb43861e-3b66-11e5-9fbf-71066ff3ab13.png" height=40 alt="graphql-ruby"/>
|
1
|
+
# graphql <img src="https://cloud.githubusercontent.com/assets/2231765/9094460/cb43861e-3b66-11e5-9fbf-71066ff3ab13.png" height="40" alt="graphql-ruby"/>
|
2
2
|
|
3
3
|
[](https://travis-ci.org/rmosolgo/graphql-ruby)
|
4
4
|
[](https://rubygems.org/gems/graphql)
|
@@ -8,16 +8,7 @@
|
|
8
8
|
|
9
9
|
A Ruby implementation of [GraphQL](http://graphql.org/).
|
10
10
|
|
11
|
-
-
|
12
|
-
- [Introduction](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/introduction.md)
|
13
|
-
- [Defining Your Schema](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/defining_your_schema.md)
|
14
|
-
- [Executing Queries](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/executing_queries.md)
|
15
|
-
- [Testing](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/testing.md)
|
16
|
-
- [Code Reuse](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/code_reuse.md)
|
17
|
-
- [Security](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/security.md)
|
18
|
-
- [Relay](http://www.rubydoc.info/github/rmosolgo/graphql-ruby/file/guides/relay.md)
|
19
|
-
|
20
|
-
|
11
|
+
- [Website](https://rmosolgo.github.io/graphql-ruby)
|
21
12
|
- [API Documentation](http://www.rubydoc.info/github/rmosolgo/graphql-ruby)
|
22
13
|
|
23
14
|
## Installation
|
@@ -103,6 +94,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
103
94
|
- __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
|
104
95
|
- __Features & patches__ are welcome! Consider discussing it in an [issue](https://github.com/rmosolgo/graphql-ruby/issues) or in the [#ruby channel on Slack](https://graphql-slack.herokuapp.com/) to make sure we're on the same page.
|
105
96
|
- __Run the tests__ with `rake test` or start up guard with `bundle exec guard`.
|
97
|
+
- __Build the site__ with `rake site:serve`, then visit `localhost:4000`.
|
106
98
|
|
107
99
|
## Related Projects
|
108
100
|
|
@@ -110,7 +102,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
110
102
|
|
111
103
|
- `graphql-ruby` + Rails demo ([src](https://github.com/rmosolgo/graphql-ruby-demo) / [heroku](http://graphql-ruby-demo.herokuapp.com))
|
112
104
|
- [`graphql-batch`](https://github.com/shopify/graphql-batch), a batched query execution strategy
|
113
|
-
- [`graphql-libgraphqlparser`](https://github.com/rmosolgo/graphql-libgraphqlparser), bindings to [libgraphqlparser](https://github.com/graphql/libgraphqlparser), a C-level parser.
|
105
|
+
- [`graphql-libgraphqlparser`](https://github.com/rmosolgo/graphql-libgraphqlparser-ruby), bindings to [libgraphqlparser](https://github.com/graphql/libgraphqlparser), a C-level parser.
|
114
106
|
|
115
107
|
### Blog Posts
|
116
108
|
|
@@ -150,3 +142,9 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
150
142
|
- Renaming fragments from local names to unique names
|
151
143
|
- Support AST subclasses? This would be hard, I think classes are used as hash keys in many places.
|
152
144
|
- Support object deep-copy (schema, type, field, argument)? To support multiple schemas based on the same types.
|
145
|
+
- Improve the website
|
146
|
+
- Feature the logo in the header
|
147
|
+
- Split `readme.md` into `index.md` (a homepage with code samples) and a technical readme (how to install, link to homepage)
|
148
|
+
- Move "Related projects" to a guide
|
149
|
+
- Revisit guides, maybe split them into smaller, more specific pages
|
150
|
+
- Put guide titles into the `<title />`
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe GraphQL::Directive do
|
4
|
-
let(:
|
4
|
+
let(:variables) { {"t" => true, "f" => false} }
|
5
|
+
let(:result) { DummySchema.execute(query_string, variables: variables) }
|
5
6
|
describe "on fields" do
|
6
7
|
let(:query_string) { %|query directives($t: Boolean!, $f: Boolean!) {
|
7
8
|
cheese(id: 1) {
|
@@ -74,4 +75,141 @@ describe GraphQL::Directive do
|
|
74
75
|
assert_equal(expected, result)
|
75
76
|
end
|
76
77
|
end
|
78
|
+
describe "merging @skip and @include" do
|
79
|
+
let(:field_included?) { r = result["data"]["cheese"]; r.has_key?('flavor') && r.has_key?('withVariables') }
|
80
|
+
let(:skip?) { false }
|
81
|
+
let(:include?) { true }
|
82
|
+
let(:variables) { {"skip" => skip?, "include" => include?} }
|
83
|
+
let(:query_string) {"
|
84
|
+
query getCheese ($include: Boolean!, $skip: Boolean!) {
|
85
|
+
cheese(id: 1) {
|
86
|
+
flavor @include(if: #{include?}) @skip(if: #{skip?}),
|
87
|
+
withVariables: flavor @include(if: $include) @skip(if: $skip)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
"}
|
91
|
+
# behavior as defined in
|
92
|
+
# https://github.com/facebook/graphql/blob/master/spec/Section%203%20--%20Type%20System.md#include
|
93
|
+
describe "when @skip=false and @include=true" do
|
94
|
+
let(:skip?) { false }
|
95
|
+
let(:include?) { true }
|
96
|
+
it "is included" do assert field_included? end
|
97
|
+
end
|
98
|
+
describe "when @skip=false and @include=false" do
|
99
|
+
let(:skip?) { false }
|
100
|
+
let(:include?) { false }
|
101
|
+
it "is not included" do assert !field_included? end
|
102
|
+
end
|
103
|
+
describe "when @skip=true and @include=true" do
|
104
|
+
let(:skip?) { true }
|
105
|
+
let(:include?) { true }
|
106
|
+
it "is not included" do assert !field_included? end
|
107
|
+
end
|
108
|
+
describe "when @skip=true and @include=false" do
|
109
|
+
let(:skip?) { true }
|
110
|
+
let(:include?) { false }
|
111
|
+
it "is not included" do assert !field_included? end
|
112
|
+
end
|
113
|
+
describe "when evaluating skip on query selection and fragment" do
|
114
|
+
describe "with @skip" do
|
115
|
+
let(:query_string) {"
|
116
|
+
query getCheese ($skip: Boolean!) {
|
117
|
+
cheese(id: 1) {
|
118
|
+
flavor,
|
119
|
+
withVariables: flavor,
|
120
|
+
...F0
|
121
|
+
}
|
122
|
+
}
|
123
|
+
fragment F0 on Cheese {
|
124
|
+
flavor @skip(if: #{skip?})
|
125
|
+
withVariables: flavor @skip(if: $skip)
|
126
|
+
}
|
127
|
+
"}
|
128
|
+
describe "and @skip=false" do
|
129
|
+
let(:skip?) { false }
|
130
|
+
it "is included" do assert field_included? end
|
131
|
+
end
|
132
|
+
describe "and @skip=true" do
|
133
|
+
let(:skip?) { true }
|
134
|
+
it "is included" do assert field_included? end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
describe "when evaluating conflicting @skip and @include on query selection and fragment" do
|
139
|
+
let(:query_string) {"
|
140
|
+
query getCheese ($include: Boolean!, $skip: Boolean!) {
|
141
|
+
cheese(id: 1) {
|
142
|
+
... on Cheese @include(if: #{include?}) {
|
143
|
+
flavor
|
144
|
+
}
|
145
|
+
withVariables: flavor @include(if: $include),
|
146
|
+
...F0
|
147
|
+
}
|
148
|
+
}
|
149
|
+
fragment F0 on Cheese {
|
150
|
+
flavor @skip(if: #{skip?}),
|
151
|
+
withVariables: flavor @skip(if: $skip)
|
152
|
+
}
|
153
|
+
"}
|
154
|
+
describe "when @skip=false and @include=true" do
|
155
|
+
let(:skip?) { false }
|
156
|
+
let(:include?) { true }
|
157
|
+
it "is included" do assert field_included? end
|
158
|
+
end
|
159
|
+
describe "when @skip=false and @include=false" do
|
160
|
+
let(:skip?) { false }
|
161
|
+
let(:include?) { false }
|
162
|
+
it "is included" do assert field_included? end
|
163
|
+
end
|
164
|
+
describe "when @skip=true and @include=true" do
|
165
|
+
let(:skip?) { true }
|
166
|
+
let(:include?) { true }
|
167
|
+
it "is included" do assert field_included? end
|
168
|
+
end
|
169
|
+
describe "when @skip=true and @include=false" do
|
170
|
+
let(:skip?) { true }
|
171
|
+
let(:include?) { false }
|
172
|
+
it "is not included" do
|
173
|
+
assert !field_included?
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "when handling multiple fields at the same level" do
|
179
|
+
describe "when at least one occurrence would be included" do
|
180
|
+
let(:query_string) {"
|
181
|
+
query getCheese ($include: Boolean!, $skip: Boolean!) {
|
182
|
+
cheese(id: 1) {
|
183
|
+
... on Cheese {
|
184
|
+
flavor
|
185
|
+
}
|
186
|
+
flavor @include(if: #{include?}),
|
187
|
+
flavor @skip(if: #{skip?}),
|
188
|
+
withVariables: flavor,
|
189
|
+
withVariables: flavor @include(if: $include),
|
190
|
+
withVariables: flavor @skip(if: $skip)
|
191
|
+
}
|
192
|
+
}
|
193
|
+
"}
|
194
|
+
let(:skip?) { true }
|
195
|
+
let(:include?) { false }
|
196
|
+
it "is included" do assert field_included? end
|
197
|
+
end
|
198
|
+
describe "when no occurrence would be included" do
|
199
|
+
let(:query_string) {"
|
200
|
+
query getCheese ($include: Boolean!, $skip: Boolean!) {
|
201
|
+
cheese(id: 1) {
|
202
|
+
flavor @include(if: #{include?}),
|
203
|
+
flavor @skip(if: #{skip?}),
|
204
|
+
withVariables: flavor @include(if: $include),
|
205
|
+
withVariables: flavor @skip(if: $skip)
|
206
|
+
}
|
207
|
+
}
|
208
|
+
"}
|
209
|
+
let(:skip?) { true }
|
210
|
+
let(:include?) { false }
|
211
|
+
it "is not included" do assert !field_included? end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
77
215
|
end
|