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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +15 -6
  3. data/lib/graphql/analysis/query_depth.rb +11 -10
  4. data/lib/graphql/directive.rb +2 -6
  5. data/lib/graphql/directive/include_directive.rb +0 -4
  6. data/lib/graphql/directive/skip_directive.rb +0 -4
  7. data/lib/graphql/execution/directive_checks.rb +22 -13
  8. data/lib/graphql/execution_error.rb +7 -0
  9. data/lib/graphql/internal_representation/node.rb +20 -2
  10. data/lib/graphql/internal_representation/rewrite.rb +66 -20
  11. data/lib/graphql/language/generation.rb +22 -8
  12. data/lib/graphql/language/nodes.rb +48 -20
  13. data/lib/graphql/language/parser.rb +436 -423
  14. data/lib/graphql/language/parser.y +22 -19
  15. data/lib/graphql/language/parser_tests.rb +131 -2
  16. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -0
  17. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  18. data/lib/graphql/query/serial_execution/value_resolution.rb +4 -1
  19. data/lib/graphql/schema/printer.rb +1 -1
  20. data/lib/graphql/static_validation/message.rb +1 -1
  21. data/lib/graphql/version.rb +1 -1
  22. data/readme.md +10 -12
  23. data/spec/graphql/directive_spec.rb +139 -1
  24. data/spec/graphql/execution_error_spec.rb +63 -3
  25. data/spec/graphql/introspection/type_type_spec.rb +2 -0
  26. data/spec/graphql/language/generation_spec.rb +55 -7
  27. data/spec/graphql/query/executor_spec.rb +4 -2
  28. data/spec/graphql/schema/catchall_middleware_spec.rb +1 -0
  29. data/spec/graphql/schema/printer_spec.rb +1 -1
  30. data/spec/graphql/schema/timeout_middleware_spec.rb +10 -5
  31. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -6
  32. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
  33. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +2 -2
  34. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +2 -2
  35. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -3
  36. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -2
  37. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +3 -3
  38. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
  39. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
  40. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +1 -1
  41. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +3 -3
  42. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +2 -2
  43. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +1 -1
  44. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
  45. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +1 -1
  46. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +4 -4
  47. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -4
  48. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +3 -3
  49. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
  50. data/spec/graphql/static_validation/validator_spec.rb +1 -1
  51. data/spec/support/dairy_app.rb +8 -0
  52. 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
- enum_name_list:
150
- enum_name { return [val[0].to_s]}
151
- | enum_name_list enum_name { return val[0] << val[1].to_s }
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[4])
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[3])
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[3])
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 enum_name_list LCURLY {
317
- return make_node(:EnumTypeDefinition, name: val[1], values: val[3])
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[3])
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 { SIT, DOWN, HEEL }
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 ['SIT', 'DOWN', 'HEEL'], type.values
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
 
@@ -35,6 +35,7 @@ module GraphQL
35
35
  def get_finished_value(raw_value)
36
36
  if raw_value.is_a?(GraphQL::ExecutionError)
37
37
  raw_value.ast_node = irep_node.ast_node
38
+ raw_value.path = irep_node.path
38
39
  execution_context.add_error(raw_value)
39
40
  end
40
41
 
@@ -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 GraphQL::Execution::DirectiveChecks.include?(irep_node, execution_context.query) && applies_to_type?(irep_node, type)
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
- "{ #{fields} }"
97
+ "{#{fields}}"
98
98
  when NonNullType
99
99
  print_value(value, type.of_type)
100
100
  when ListType
@@ -26,7 +26,7 @@ module GraphQL
26
26
  {
27
27
  "message" => message,
28
28
  "locations" => locations,
29
- "path" => path,
29
+ "fields" => path,
30
30
  }
31
31
  end
32
32
 
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.18.14"
2
+ VERSION = "0.18.15"
3
3
  end
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
  [![Build Status](https://travis-ci.org/rmosolgo/graphql-ruby.svg?branch=master)](https://travis-ci.org/rmosolgo/graphql-ruby)
4
4
  [![Gem Version](https://badge.fury.io/rb/graphql.svg)](https://rubygems.org/gems/graphql)
@@ -8,16 +8,7 @@
8
8
 
9
9
  A Ruby implementation of [GraphQL](http://graphql.org/).
10
10
 
11
- - Guides
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(:result) { DummySchema.execute(query_string, variables: {"t" => true, "f" => false}) }
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