graphql 0.17.2 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -0
  3. data/lib/graphql/analysis/query_depth.rb +1 -1
  4. data/lib/graphql/base_type.rb +25 -1
  5. data/lib/graphql/define.rb +2 -0
  6. data/lib/graphql/define/assign_connection.rb +11 -0
  7. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  8. data/lib/graphql/define/assign_object_field.rb +21 -20
  9. data/lib/graphql/define/defined_object_proxy.rb +2 -2
  10. data/lib/graphql/define/instance_definable.rb +13 -3
  11. data/lib/graphql/field.rb +1 -1
  12. data/lib/graphql/language/generation.rb +57 -6
  13. data/lib/graphql/language/lexer.rb +434 -212
  14. data/lib/graphql/language/lexer.rl +18 -0
  15. data/lib/graphql/language/nodes.rb +75 -0
  16. data/lib/graphql/language/parser.rb +853 -341
  17. data/lib/graphql/language/parser.y +114 -17
  18. data/lib/graphql/query.rb +15 -1
  19. data/lib/graphql/relay.rb +13 -0
  20. data/lib/graphql/relay/array_connection.rb +80 -0
  21. data/lib/graphql/relay/base_connection.rb +138 -0
  22. data/lib/graphql/relay/connection_field.rb +54 -0
  23. data/lib/graphql/relay/connection_type.rb +25 -0
  24. data/lib/graphql/relay/edge.rb +22 -0
  25. data/lib/graphql/relay/edge_type.rb +14 -0
  26. data/lib/graphql/relay/global_id_resolve.rb +15 -0
  27. data/lib/graphql/relay/global_node_identification.rb +124 -0
  28. data/lib/graphql/relay/mutation.rb +146 -0
  29. data/lib/graphql/relay/page_info.rb +13 -0
  30. data/lib/graphql/relay/relation_connection.rb +98 -0
  31. data/lib/graphql/schema.rb +3 -0
  32. data/lib/graphql/schema/printer.rb +12 -2
  33. data/lib/graphql/static_validation/message.rb +9 -5
  34. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  35. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  37. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +7 -7
  38. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  39. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  40. data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -6
  41. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +17 -9
  42. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  43. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -1
  44. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  45. data/lib/graphql/static_validation/rules/fragments_are_used.rb +17 -6
  46. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  47. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -2
  48. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -5
  49. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  50. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +12 -11
  51. data/lib/graphql/static_validation/type_stack.rb +33 -2
  52. data/lib/graphql/static_validation/validation_context.rb +5 -0
  53. data/lib/graphql/version.rb +1 -1
  54. data/readme.md +16 -4
  55. data/spec/graphql/analysis/analyze_query_spec.rb +31 -2
  56. data/spec/graphql/analysis/query_complexity_spec.rb +24 -0
  57. data/spec/graphql/argument_spec.rb +1 -1
  58. data/spec/graphql/define/instance_definable_spec.rb +9 -0
  59. data/spec/graphql/field_spec.rb +1 -1
  60. data/spec/graphql/internal_representation/rewrite_spec.rb +3 -3
  61. data/spec/graphql/language/generation_spec.rb +25 -4
  62. data/spec/graphql/language/parser_spec.rb +116 -1
  63. data/spec/graphql/query_spec.rb +10 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +164 -0
  65. data/spec/graphql/relay/connection_type_spec.rb +37 -0
  66. data/spec/graphql/relay/global_node_identification_spec.rb +149 -0
  67. data/spec/graphql/relay/mutation_spec.rb +55 -0
  68. data/spec/graphql/relay/page_info_spec.rb +106 -0
  69. data/spec/graphql/relay/relation_connection_spec.rb +348 -0
  70. data/spec/graphql/schema/printer_spec.rb +8 -0
  71. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  72. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +12 -6
  73. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +8 -4
  74. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  75. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  76. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -2
  77. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -2
  78. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  79. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +5 -3
  80. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  81. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +5 -2
  82. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +10 -2
  83. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +6 -3
  84. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +8 -4
  85. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  86. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +6 -3
  87. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  88. data/spec/spec_helper.rb +7 -0
  89. data/spec/support/dairy_app.rb +11 -10
  90. data/spec/support/star_wars_data.rb +65 -58
  91. data/spec/support/star_wars_schema.rb +192 -54
  92. metadata +84 -2
@@ -1,76 +1,214 @@
1
- # Based on the graphql-js example
2
- # https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsSchema.js
3
- require_relative "./star_wars_data"
1
+ # Adapted from graphql-relay-js
2
+ # https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/starWarsSchema.js
4
3
 
5
- EpisodeEnum = GraphQL::EnumType.define do
6
- name("Episode")
7
- description("One of the films in the Star Wars Trilogy.")
4
+ # This object exposes helpers for working with global IDs:
5
+ # - global id creation & "decrypting"
6
+ # - a find-object-by-global ID field
7
+ # - an interface for Relay ObjectTypes to implement
8
+ # See global_node_identification.rb for the full API.
9
+ NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
10
+ object_from_id -> (node_id, ctx) do
11
+ type_name, id = NodeIdentification.from_global_id(node_id)
12
+ STAR_WARS_DATA[type_name][id]
13
+ end
8
14
 
9
- value("NEWHOPE", "Released in 1977", value: 4)
10
- value("EMPIRE", "Released in 1980", value: 5)
11
- value("JEDI", "Released in 1983", value: 6)
15
+ type_from_object -> (object) do
16
+ if object == :test_error
17
+ :not_a_type
18
+ elsif object.is_a?(Base)
19
+ BaseType
20
+ elsif STAR_WARS_DATA["Faction"].values.include?(object)
21
+ Faction
22
+ elsif STAR_WARS_DATA["Ship"].values.include?(object)
23
+ Ship
24
+ else
25
+ nil
26
+ end
27
+ end
12
28
  end
13
29
 
14
- CharacterInterface = GraphQL::InterfaceType.define do
15
- name("Character")
16
- description("A character in the Star Wars Trilogy.")
30
+ Ship = GraphQL::ObjectType.define do
31
+ name "Ship"
32
+ interfaces [NodeIdentification.interface]
33
+ global_id_field :id
34
+ field :name, types.String
35
+ end
17
36
 
18
- field :id, !types.String, "The id of the character."
19
- field :name, types.String, "The name of the Character."
20
- field :friends, -> { types[CharacterInterface] }, "The friends of the character, or an empty list if they have none."
21
- field :appearsIn, types[EpisodeEnum], "Which movies they appear in."
37
+ BaseType = GraphQL::ObjectType.define do
38
+ name "Base"
39
+ interfaces [NodeIdentification.interface]
40
+ global_id_field :id
41
+ field :name, types.String
42
+ field :planet, types.String
22
43
  end
23
44
 
24
- HumanType = GraphQL::ObjectType.define do
25
- name("Human")
26
- description("A humanoid creature in the Star Wars universe.")
27
- field :id, !types.String, "The id of the human."
28
- field :name, types.String, "The name of the human."
29
- field :friends do
30
- type(types[CharacterInterface])
31
- description("The friends of the human, or an empty list if they have none.")
32
- resolve(GET_FRIENDS)
45
+ # Use an optional block to add fields to the connection type:
46
+ BaseConnectionWithTotalCountType = BaseType.define_connection do
47
+ name "BasesConnectionWithTotalCount"
48
+ field :totalCount do
49
+ type types.Int
50
+ resolve -> (obj, args, ctx) { obj.object.count }
33
51
  end
34
- field :appearsIn, types[EpisodeEnum], "Which movies they appear in."
35
- field :homePlanet, types.String, "The home planet of the human, or null if unknown."
52
+ end
36
53
 
37
- interfaces([CharacterInterface])
54
+ class CustomBaseEdge < GraphQL::Relay::Edge
55
+ def upcased_name
56
+ node.name.upcase
57
+ end
58
+
59
+ def upcased_parent_name
60
+ parent.name.upcase
61
+ end
38
62
  end
39
63
 
40
- DroidType = GraphQL::ObjectType.define do
41
- name("Droid")
42
- description("A mechanical creature in the Star Wars universe.")
43
- field :id, !types.String, "The id of the droid."
44
- field :name, types.String, "The name of the droid."
45
- field :friends do
46
- type(types[CharacterInterface])
47
- description("The friends of the droid, or an empty list if they have none.")
48
- resolve(GET_FRIENDS)
64
+ CustomBaseEdgeType = BaseType.define_edge do
65
+ name "CustomBaseEdge"
66
+ field :upcasedName, types.String, property: :upcased_name
67
+ field :upcasedParentName, types.String, property: :upcased_parent_name
68
+ field :edgeClassName, types.String do
69
+ resolve -> (obj, args, ctx) { obj.class.name }
49
70
  end
50
- field :appearsIn, types[EpisodeEnum], "Which movies they appear in."
51
- field :primaryFunction, types.String, "The primary function of the droid."
71
+ end
52
72
 
53
- interfaces([CharacterInterface])
73
+ CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBaseEdge, edge_type: CustomBaseEdgeType) do
74
+ name "CustomEdgeBaseConnection"
75
+
76
+ field :totalCountTimes100 do
77
+ type types.Int
78
+ resolve -> (obj, args, ctx) { obj.object.count * 100 }
79
+ end
54
80
  end
55
81
 
56
- class FindRecordField
57
- def self.create(return_type, data)
58
- GraphQL::Field.define do
59
- type(return_type)
60
- argument :id, !types.String, "The id of the #{return_type.name}."
61
- resolve -> (obj, args, ctx) { data[args["id"]] }
62
- end
82
+ Faction = GraphQL::ObjectType.define do
83
+ name "Faction"
84
+ interfaces [NodeIdentification.interface]
85
+
86
+ field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type_name: "Faction", property: :id)
87
+ field :name, types.String
88
+ connection :ships, Ship.connection_type do
89
+ resolve -> (obj, args, ctx) {
90
+ all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
91
+ if args[:nameIncludes]
92
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
93
+ end
94
+ all_ships
95
+ }
96
+ # You can define arguments here and use them in the connection
97
+ argument :nameIncludes, types.String
98
+ end
99
+ connection :shipsWithMaxPageSize, Ship.connection_type, max_page_size: 2 do
100
+ resolve -> (obj, args, ctx) {
101
+ all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
102
+ if args[:nameIncludes]
103
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
104
+ end
105
+ all_ships
106
+ }
107
+ # You can define arguments here and use them in the connection
108
+ argument :nameIncludes, types.String
109
+ end
110
+
111
+ connection :bases, BaseConnectionWithTotalCountType do
112
+ # Resolve field should return an Array, the Connection
113
+ # will do the rest!
114
+ resolve -> (obj, args, ctx) {
115
+ all_bases = Base.where(id: obj.bases)
116
+ if args[:nameIncludes]
117
+ all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
118
+ end
119
+ all_bases
120
+ }
121
+ argument :nameIncludes, types.String
122
+ end
123
+
124
+ connection :basesClone, BaseType.connection_type
125
+ connection :basesByName, BaseType.connection_type, property: :bases do
126
+ argument :order, types.String, default_value: "name"
127
+ resolve -> (obj, args, ctx) {
128
+ if args[:order].present?
129
+ obj.bases.order(args[:order])
130
+ else
131
+ obj.bases
132
+ end
133
+ }
63
134
  end
135
+
136
+ connection :basesWithMaxLimitRelation, BaseType.connection_type, max_page_size: 2 do
137
+ resolve -> (object, args, context) { Base.all }
138
+ end
139
+
140
+ connection :basesWithMaxLimitArray, BaseType.connection_type, max_page_size: 2 do
141
+ resolve -> (object, args, context) { Base.all.to_a }
142
+ end
143
+
144
+ connection :basesAsSequelDataset, BaseConnectionWithTotalCountType do
145
+ argument :nameIncludes, types.String
146
+ resolve -> (obj, args, ctx) {
147
+ all_bases = SequelBase.where(faction_id: obj.id)
148
+ if args[:nameIncludes]
149
+ all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
150
+ end
151
+ all_bases
152
+ }
153
+ end
154
+
155
+ connection :basesWithCustomEdge, CustomEdgeBaseConnectionType, property: :bases
156
+ end
157
+
158
+ # Define a mutation. It will also:
159
+ # - define a derived InputObjectType
160
+ # - define a derived ObjectType (for return)
161
+ # - define a field, accessible from {Mutation#field}
162
+ #
163
+ # The resolve proc takes `inputs, ctx`, where:
164
+ # - `inputs` has the keys defined with `input_field`
165
+ # - `ctx` is the Query context (like normal fields)
166
+ #
167
+ # Notice that you leave out clientMutationId.
168
+ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
169
+ # Used as the root for derived types:
170
+ name "IntroduceShip"
171
+ description "Add a ship to this faction"
172
+
173
+ # Nested under `input` in the query:
174
+ input_field :shipName, !types.String
175
+ input_field :factionId, !types.ID
176
+
177
+ # Result may have access to these fields:
178
+ return_field :ship, Ship
179
+ return_field :faction, Faction
180
+
181
+ # Here's the mutation operation:
182
+ resolve -> (inputs, ctx) {
183
+ faction_id = inputs["factionId"]
184
+ ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
185
+ faction = STAR_WARS_DATA["Faction"][faction_id]
186
+ { ship: ship, faction: faction }
187
+ }
64
188
  end
65
189
 
190
+ QueryType = GraphQL::ObjectType.define do
191
+ name "Query"
192
+ field :rebels, Faction do
193
+ resolve -> (obj, args, ctx) { STAR_WARS_DATA["Faction"]["1"]}
194
+ end
66
195
 
67
- StarWarsQueryType = GraphQL::ObjectType.define do
68
- name("Query")
69
- field :hero do
70
- argument :episode, EpisodeEnum, "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode"
71
- resolve -> (obj, args, ctx) { args["episode"] == 5 ? luke : artoo }
196
+ field :empire, Faction do
197
+ resolve -> (obj, args, ctx) { STAR_WARS_DATA["Faction"]["2"]}
72
198
  end
73
199
 
74
- field :human, field: FindRecordField.create(HumanType, HUMAN_DATA)
75
- field :droid, field: FindRecordField.create(DroidType, DROID_DATA)
200
+ field :largestBase, BaseType do
201
+ resolve -> (obj, args, ctx) { Base.find(3) }
202
+ end
203
+
204
+ field :node, field: NodeIdentification.field
205
+ end
206
+
207
+ MutationType = GraphQL::ObjectType.define do
208
+ name "Mutation"
209
+ # The mutation object exposes a field:
210
+ field :introduceShip, field: IntroduceShipMutation.field
76
211
  end
212
+
213
+ StarWarsSchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)
214
+ StarWarsSchema.node_identification = NodeIdentification
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: 0.17.2
4
+ version: 0.18.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-07-26 00:00:00.000000000 Z
11
+ date: 2016-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codeclimate-test-reporter
@@ -178,6 +178,62 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '11.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: activerecord
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: appraisal
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: sequel
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: sqlite3
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
181
237
  description: A GraphQL server implementation for Ruby. Includes schema definition,
182
238
  query parsing, static validation, type definition, and query execution.
183
239
  email:
@@ -200,7 +256,9 @@ files:
200
256
  - lib/graphql/boolean_type.rb
201
257
  - lib/graphql/define.rb
202
258
  - lib/graphql/define/assign_argument.rb
259
+ - lib/graphql/define/assign_connection.rb
203
260
  - lib/graphql/define/assign_enum_value.rb
261
+ - lib/graphql/define/assign_global_id_field.rb
204
262
  - lib/graphql/define/assign_object_field.rb
205
263
  - lib/graphql/define/assignment_dictionary.rb
206
264
  - lib/graphql/define/defined_object_proxy.rb
@@ -271,6 +329,18 @@ files:
271
329
  - lib/graphql/query/type_resolver.rb
272
330
  - lib/graphql/query/variable_validation_error.rb
273
331
  - lib/graphql/query/variables.rb
332
+ - lib/graphql/relay.rb
333
+ - lib/graphql/relay/array_connection.rb
334
+ - lib/graphql/relay/base_connection.rb
335
+ - lib/graphql/relay/connection_field.rb
336
+ - lib/graphql/relay/connection_type.rb
337
+ - lib/graphql/relay/edge.rb
338
+ - lib/graphql/relay/edge_type.rb
339
+ - lib/graphql/relay/global_id_resolve.rb
340
+ - lib/graphql/relay/global_node_identification.rb
341
+ - lib/graphql/relay/mutation.rb
342
+ - lib/graphql/relay/page_info.rb
343
+ - lib/graphql/relay/relation_connection.rb
274
344
  - lib/graphql/scalar_type.rb
275
345
  - lib/graphql/schema.rb
276
346
  - lib/graphql/schema/catchall_middleware.rb
@@ -352,6 +422,12 @@ files:
352
422
  - spec/graphql/query/type_resolver_spec.rb
353
423
  - spec/graphql/query/variables_spec.rb
354
424
  - spec/graphql/query_spec.rb
425
+ - spec/graphql/relay/array_connection_spec.rb
426
+ - spec/graphql/relay/connection_type_spec.rb
427
+ - spec/graphql/relay/global_node_identification_spec.rb
428
+ - spec/graphql/relay/mutation_spec.rb
429
+ - spec/graphql/relay/page_info_spec.rb
430
+ - spec/graphql/relay/relation_connection_spec.rb
355
431
  - spec/graphql/scalar_type_spec.rb
356
432
  - spec/graphql/schema/catchall_middleware_spec.rb
357
433
  - spec/graphql/schema/middleware_chain_spec.rb
@@ -452,6 +528,12 @@ test_files:
452
528
  - spec/graphql/query/type_resolver_spec.rb
453
529
  - spec/graphql/query/variables_spec.rb
454
530
  - spec/graphql/query_spec.rb
531
+ - spec/graphql/relay/array_connection_spec.rb
532
+ - spec/graphql/relay/connection_type_spec.rb
533
+ - spec/graphql/relay/global_node_identification_spec.rb
534
+ - spec/graphql/relay/mutation_spec.rb
535
+ - spec/graphql/relay/page_info_spec.rb
536
+ - spec/graphql/relay/relation_connection_spec.rb
455
537
  - spec/graphql/scalar_type_spec.rb
456
538
  - spec/graphql/schema/catchall_middleware_spec.rb
457
539
  - spec/graphql/schema/middleware_chain_spec.rb