graphql 1.5.15 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +4 -19
  3. data/lib/graphql/analysis/analyze_query.rb +27 -2
  4. data/lib/graphql/analysis/query_complexity.rb +10 -11
  5. data/lib/graphql/argument.rb +7 -6
  6. data/lib/graphql/backwards_compatibility.rb +47 -0
  7. data/lib/graphql/compatibility/execution_specification.rb +14 -0
  8. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +6 -1
  9. data/lib/graphql/compatibility/lazy_execution_specification.rb +19 -0
  10. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +15 -6
  11. data/lib/graphql/directive.rb +1 -6
  12. data/lib/graphql/execution.rb +1 -0
  13. data/lib/graphql/execution/execute.rb +174 -160
  14. data/lib/graphql/execution/field_result.rb +5 -1
  15. data/lib/graphql/execution/lazy.rb +2 -2
  16. data/lib/graphql/execution/lazy/resolve.rb +8 -11
  17. data/lib/graphql/execution/multiplex.rb +134 -0
  18. data/lib/graphql/execution/selection_result.rb +5 -0
  19. data/lib/graphql/field.rb +1 -8
  20. data/lib/graphql/filter.rb +53 -0
  21. data/lib/graphql/internal_representation/node.rb +11 -6
  22. data/lib/graphql/internal_representation/rewrite.rb +3 -3
  23. data/lib/graphql/query.rb +160 -78
  24. data/lib/graphql/query/arguments.rb +14 -25
  25. data/lib/graphql/query/arguments_cache.rb +6 -13
  26. data/lib/graphql/query/context.rb +28 -10
  27. data/lib/graphql/query/executor.rb +1 -0
  28. data/lib/graphql/query/literal_input.rb +10 -4
  29. data/lib/graphql/query/null_context.rb +1 -1
  30. data/lib/graphql/query/serial_execution/field_resolution.rb +5 -1
  31. data/lib/graphql/query/validation_pipeline.rb +12 -7
  32. data/lib/graphql/query/variables.rb +1 -1
  33. data/lib/graphql/rake_task.rb +140 -0
  34. data/lib/graphql/relay/array_connection.rb +29 -48
  35. data/lib/graphql/relay/base_connection.rb +9 -7
  36. data/lib/graphql/relay/mutation.rb +0 -11
  37. data/lib/graphql/relay/mutation/instrumentation.rb +2 -2
  38. data/lib/graphql/relay/mutation/resolve.rb +7 -10
  39. data/lib/graphql/relay/relation_connection.rb +98 -61
  40. data/lib/graphql/scalar_type.rb +1 -15
  41. data/lib/graphql/schema.rb +90 -25
  42. data/lib/graphql/schema/build_from_definition.rb +22 -23
  43. data/lib/graphql/schema/build_from_definition/resolve_map.rb +70 -0
  44. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
  45. data/lib/graphql/schema/middleware_chain.rb +1 -1
  46. data/lib/graphql/schema/printer.rb +2 -1
  47. data/lib/graphql/schema/timeout_middleware.rb +6 -6
  48. data/lib/graphql/schema/type_map.rb +1 -1
  49. data/lib/graphql/schema/warden.rb +5 -9
  50. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  51. data/lib/graphql/version.rb +1 -1
  52. data/spec/graphql/analysis/analyze_query_spec.rb +2 -2
  53. data/spec/graphql/analysis/max_query_complexity_spec.rb +28 -0
  54. data/spec/graphql/argument_spec.rb +3 -3
  55. data/spec/graphql/execution/lazy_spec.rb +8 -114
  56. data/spec/graphql/execution/multiplex_spec.rb +131 -0
  57. data/spec/graphql/internal_representation/rewrite_spec.rb +10 -0
  58. data/spec/graphql/query/arguments_spec.rb +14 -16
  59. data/spec/graphql/query/context_spec.rb +14 -1
  60. data/spec/graphql/query/literal_input_spec.rb +19 -13
  61. data/spec/graphql/query/variables_spec.rb +1 -1
  62. data/spec/graphql/query_spec.rb +12 -1
  63. data/spec/graphql/rake_task_spec.rb +57 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +24 -3
  65. data/spec/graphql/relay/connection_instrumentation_spec.rb +23 -0
  66. data/spec/graphql/relay/mutation_spec.rb +2 -10
  67. data/spec/graphql/relay/page_info_spec.rb +2 -2
  68. data/spec/graphql/relay/relation_connection_spec.rb +167 -3
  69. data/spec/graphql/schema/build_from_definition_spec.rb +93 -19
  70. data/spec/graphql/schema/warden_spec.rb +80 -0
  71. data/spec/graphql/schema_spec.rb +26 -2
  72. data/spec/spec_helper.rb +4 -2
  73. data/spec/support/lazy_helpers.rb +152 -0
  74. data/spec/support/star_wars/schema.rb +23 -0
  75. metadata +28 -3
  76. data/lib/graphql/schema/mask.rb +0 -55
@@ -26,14 +26,16 @@ module GraphQL
26
26
  # @return [subclass of BaseConnection] a connection Class for wrapping `nodes`
27
27
  def connection_for_nodes(nodes)
28
28
  # Check for class _names_ because classes can be redefined in Rails development
29
- nodes.class.ancestors.each do |ancestor|
30
- conn_impl = CONNECTION_IMPLEMENTATIONS[ancestor.name]
31
- if conn_impl
32
- return conn_impl
33
- end
29
+ ancestor_names = nodes.class.ancestors.map(&:name)
30
+ implementation_class_name = ancestor_names.find do |ancestor_class_name|
31
+ CONNECTION_IMPLEMENTATIONS.include? ancestor_class_name
32
+ end
33
+
34
+ if implementation_class_name.nil?
35
+ raise("No connection implementation to wrap #{nodes.class} (#{nodes})")
36
+ else
37
+ CONNECTION_IMPLEMENTATIONS[implementation_class_name]
34
38
  end
35
- # Should have found a connection during the loop:
36
- raise("No connection implementation to wrap #{nodes.class} (#{nodes})")
37
39
  end
38
40
 
39
41
  # Add `connection_class` as the connection wrapper for `nodes_class`
@@ -184,17 +184,6 @@ module GraphQL
184
184
  def result_class
185
185
  @result_class ||= Result.define_subclass(self)
186
186
  end
187
-
188
- private
189
-
190
- def get_arity(callable)
191
- case callable
192
- when Proc
193
- callable.arity
194
- else
195
- callable.method(:call).arity
196
- end
197
- end
198
187
  end
199
188
  end
200
189
  end
@@ -11,8 +11,8 @@ module GraphQL
11
11
  # giving users access to the original resolve function in earlier instrumentation.
12
12
  def self.instrument(type, field)
13
13
  if field.mutation
14
- new_resolve = Mutation::Resolve.new(field.mutation, field.resolve_proc, eager: true)
15
- new_lazy_resolve = Mutation::Resolve.new(field.mutation, field.lazy_resolve_proc, eager: false)
14
+ new_resolve = Mutation::Resolve.new(field.mutation, field.resolve_proc)
15
+ new_lazy_resolve = Mutation::Resolve.new(field.mutation, field.lazy_resolve_proc)
16
16
  field.redefine(resolve: new_resolve, lazy_resolve: new_lazy_resolve)
17
17
  else
18
18
  field
@@ -7,38 +7,35 @@ module GraphQL
7
7
  # Also, pass the `clientMutationId` to that result object.
8
8
  # @api private
9
9
  class Resolve
10
- def initialize(mutation, resolve, eager:)
10
+ def initialize(mutation, resolve)
11
11
  @mutation = mutation
12
12
  @resolve = resolve
13
13
  @wrap_result = mutation.has_generated_return_type?
14
- @eager = eager
15
14
  end
16
15
 
17
16
  def call(obj, args, ctx)
18
- error_raised = false
19
- begin
20
- mutation_result = @resolve.call(obj, args[:input], ctx)
17
+ mutation_result = begin
18
+ @resolve.call(obj, args[:input], ctx)
21
19
  rescue GraphQL::ExecutionError => err
22
- mutation_result = err
23
- error_raised = true
20
+ err
24
21
  end
25
22
 
26
23
  if ctx.schema.lazy?(mutation_result)
27
24
  mutation_result
28
25
  else
29
- build_result(mutation_result, args, ctx, raised: error_raised)
26
+ build_result(mutation_result, args, ctx)
30
27
  end
31
28
  end
32
29
 
33
30
  private
34
31
 
35
- def build_result(mutation_result, args, ctx, raised: false)
32
+ def build_result(mutation_result, args, ctx)
36
33
  if mutation_result.is_a?(GraphQL::ExecutionError)
37
34
  ctx.add_error(mutation_result)
38
35
  mutation_result = nil
39
36
  end
40
37
 
41
- if @eager && raised
38
+ if mutation_result.nil?
42
39
  nil
43
40
  elsif @wrap_result
44
41
  if mutation_result && !mutation_result.is_a?(Hash)
@@ -11,102 +11,139 @@ module GraphQL
11
11
  if item_index.nil?
12
12
  raise("Can't generate cursor, item not found in connection: #{item}")
13
13
  else
14
- offset = starting_offset + item_index + 1
14
+ offset = item_index + 1 + ((relation_offset(paged_nodes) || 0) - (relation_offset(sliced_nodes) || 0))
15
+
16
+ if after
17
+ offset += offset_from_cursor(after)
18
+ elsif before
19
+ offset += offset_from_cursor(before) - 1 - sliced_nodes_count
20
+ end
21
+
15
22
  encode(offset.to_s)
16
23
  end
17
24
  end
18
25
 
19
26
  def has_next_page
20
- !!(first && paged_nodes && @has_next_page)
27
+ !!(first && sliced_nodes_count > first)
21
28
  end
22
29
 
23
30
  def has_previous_page
24
- !!(last && starting_offset > 0)
31
+ !!(last && sliced_nodes_count > last)
25
32
  end
26
33
 
27
- private
34
+ def first
35
+ return @first if defined? @first
28
36
 
29
- # If a relation contains a `.group` clause, a `.count` will return a Hash.
30
- def count(nodes)
31
- count_or_hash = nodes.count
32
- count_or_hash.is_a?(Integer) ? count_or_hash : count_or_hash.length
37
+ @first = get_limited_arg(:first)
38
+ @first = max_page_size if @first && max_page_size && @first > max_page_size
39
+ @first
33
40
  end
34
41
 
42
+ def last
43
+ return @last if defined? @last
44
+
45
+ @last = get_limited_arg(:last)
46
+ @last = max_page_size if @last && max_page_size && @last > max_page_size
47
+ @last
48
+ end
49
+
50
+ private
51
+
35
52
  # apply first / last limit results
36
53
  def paged_nodes
37
- @paged_nodes ||= begin
38
- if limit
39
- limit_more = limit + 1
40
- more_nodes = sliced_nodes.limit(limit_more).to_a
41
- if more_nodes.size > limit
42
- @has_next_page = true
43
- more_nodes[0..-2]
44
- else
45
- @has_next_page = false
46
- more_nodes
54
+ return @paged_nodes if defined? @paged_nodes
55
+
56
+ items = sliced_nodes
57
+
58
+ if first
59
+ if relation_limit(items).nil? || relation_limit(items) > first
60
+ items = items.limit(first)
61
+ end
62
+ end
63
+
64
+ if last
65
+ if relation_limit(items)
66
+ if last <= relation_limit(items)
67
+ offset = (relation_offset(items) || 0) + (relation_limit(items) - last)
68
+ items = items.offset(offset).limit(last)
47
69
  end
48
70
  else
49
- @has_next_page = false
50
- sliced_nodes
71
+ offset = (relation_offset(items) || 0) + relation_count(items) - last
72
+ items = items.offset(offset).limit(last)
51
73
  end
52
74
  end
53
- end
54
75
 
55
- # Apply cursors to edges
56
- def sliced_nodes
57
- @sliced_nodes ||= nodes.offset(starting_offset)
58
- end
76
+ if max_page_size && !first && !last
77
+ if relation_limit(items).nil? || relation_limit(items) > max_page_size
78
+ items = items.limit(max_page_size)
79
+ end
80
+ end
59
81
 
60
- def offset_from_cursor(cursor)
61
- decode(cursor).to_i
82
+ @paged_nodes = items
62
83
  end
63
84
 
64
- def starting_offset
65
- @starting_offset ||= begin
66
- if before
67
- [previous_offset, 0].max
68
- elsif last
69
- [count(nodes) - last, 0].max
70
- else
71
- previous_offset
72
- end
85
+ def relation_offset(relation)
86
+ case relation
87
+ when ActiveRecord::Relation
88
+ relation.offset_value
89
+ when Sequel::Dataset
90
+ relation.opts[:offset]
73
91
  end
74
92
  end
75
93
 
76
- # Offset from the previous selection, if there was one
77
- # Otherwise, zero
78
- def previous_offset
79
- @previous_offset ||= if after
80
- offset_from_cursor(after)
81
- elsif before
82
- prev_page_size = [max_page_size, last].compact.min || 0
83
- offset_from_cursor(before) - prev_page_size - 1
84
- else
85
- 0
94
+ def relation_limit(relation)
95
+ case relation
96
+ when ActiveRecord::Relation
97
+ relation.limit_value
98
+ when Sequel::Dataset
99
+ relation.opts[:limit]
86
100
  end
87
101
  end
88
102
 
89
- # Limit to apply to this query:
90
- # - find a value from the query
91
- # - don't exceed max_page_size
92
- # - otherwise, don't limit
93
- def limit
94
- @limit ||= begin
95
- limit_from_arguments = if first
96
- first
103
+ # If a relation contains a `.group` clause, a `.count` will return a Hash.
104
+ def relation_count(relation)
105
+ count_or_hash = relation.count
106
+ count_or_hash.is_a?(Integer) ? count_or_hash : count_or_hash.length
107
+ end
108
+
109
+ # Apply cursors to edges
110
+ def sliced_nodes
111
+ return @sliced_nodes if defined? @sliced_nodes
112
+
113
+ @sliced_nodes = nodes
114
+
115
+ if after
116
+ offset = (relation_offset(@sliced_nodes) || 0) + offset_from_cursor(after)
117
+ @sliced_nodes = @sliced_nodes.offset(offset) if after
118
+ end
119
+
120
+ if before && after
121
+ if offset_from_cursor(after) < offset_from_cursor(before)
122
+ @sliced_nodes = @sliced_nodes.limit(offset_from_cursor(before) - offset_from_cursor(after) - 1)
97
123
  else
98
- if previous_offset < 0
99
- previous_offset + (last ? last : 0)
100
- else
101
- last
102
- end
124
+ @sliced_nodes = @sliced_nodes.limit(0)
103
125
  end
104
- [limit_from_arguments, max_page_size].compact.min
126
+ elsif before
127
+ @sliced_nodes = @sliced_nodes.limit(offset_from_cursor(before) - 1)
105
128
  end
129
+
130
+ @sliced_nodes
131
+ end
132
+
133
+ def sliced_nodes_count
134
+ return @sliced_nodes_count if defined? @sliced_nodes_count
135
+
136
+ # If a relation contains a `.group` clause, a `.count` will return a Hash.
137
+ @sliced_nodes_count = relation_count(sliced_nodes)
138
+ end
139
+
140
+ def offset_from_cursor(cursor)
141
+ decode(cursor).to_i
106
142
  end
107
143
 
108
144
  def paged_nodes_array
109
- @paged_nodes_array ||= paged_nodes.to_a
145
+ return @paged_nodes_array if defined?(@paged_nodes_array)
146
+ @paged_nodes_array = paged_nodes.to_a
110
147
  end
111
148
  end
112
149
 
@@ -80,22 +80,8 @@ module GraphQL
80
80
 
81
81
  private
82
82
 
83
- def get_arity(callable)
84
- case callable
85
- when Proc
86
- callable.arity
87
- else
88
- callable.method(:call).arity
89
- end
90
- end
91
-
92
83
  def ensure_two_arg(callable, method_name)
93
- if get_arity(callable) == 1
94
- warn("Scalar coerce functions receive two values (`val` and `ctx`), one-argument functions are deprecated (see #{name}.#{method_name}).")
95
- ->(val, ctx) { callable.call(val) }
96
- else
97
- callable
98
- end
84
+ GraphQL::BackwardsCompatibility.wrap_arity(callable, from: 1, to: 2, name: "#{name}.#{method_name}(val, ctx)")
99
85
  end
100
86
 
101
87
  def coerce_non_null_input(value, ctx)
@@ -6,7 +6,6 @@ require "graphql/schema/default_type_error"
6
6
  require "graphql/schema/invalid_type_error"
7
7
  require "graphql/schema/instrumented_field_map"
8
8
  require "graphql/schema/middleware_chain"
9
- require "graphql/schema/mask"
10
9
  require "graphql/schema/null_mask"
11
10
  require "graphql/schema/possible_types"
12
11
  require "graphql/schema/rescue_middleware"
@@ -63,8 +62,14 @@ module GraphQL
63
62
  :default_mask,
64
63
  :cursor_encoder,
65
64
  directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.name] = d; m }},
66
- instrument: ->(schema, type, instrumenter) { schema.instrumenters[type] << instrumenter },
65
+ instrument: ->(schema, type, instrumenter, after_built_ins: false) {
66
+ if type == :field && after_built_ins
67
+ type = :field_after_built_ins
68
+ end
69
+ schema.instrumenters[type] << instrumenter
70
+ },
67
71
  query_analyzer: ->(schema, analyzer) { schema.query_analyzers << analyzer },
72
+ multiplex_analyzer: ->(schema, analyzer) { schema.multiplex_analyzers << analyzer },
68
73
  middleware: ->(schema, middleware) { schema.middleware << middleware },
69
74
  lazy_resolve: ->(schema, lazy_class, lazy_value_method) { schema.lazy_methods.set(lazy_class, lazy_value_method) },
70
75
  rescue_from: ->(schema, err_class, &block) { schema.rescue_from(err_class, &block)}
@@ -74,7 +79,7 @@ module GraphQL
74
79
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
75
80
  :max_depth, :max_complexity,
76
81
  :orphan_types, :directives,
77
- :query_analyzers, :instrumenters, :lazy_methods,
82
+ :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
78
83
  :cursor_encoder,
79
84
  :raise_definition_error
80
85
 
@@ -89,6 +94,10 @@ module GraphQL
89
94
  attr_accessor :default_execution_strategy
90
95
  end
91
96
 
97
+ def default_filter
98
+ GraphQL::Filter.new(except: default_mask)
99
+ end
100
+
92
101
  self.default_execution_strategy = GraphQL::Execution::Execute
93
102
 
94
103
  BUILT_IN_TYPES = Hash[[INT_TYPE, STRING_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, ID_TYPE].map{ |type| [type.name, type] }]
@@ -104,6 +113,7 @@ module GraphQL
104
113
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
105
114
  @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
106
115
  @query_analyzers = []
116
+ @multiplex_analyzers = []
107
117
  @resolve_type_proc = nil
108
118
  @object_from_id_proc = nil
109
119
  @id_from_object_proc = nil
@@ -127,6 +137,7 @@ module GraphQL
127
137
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
128
138
  @middleware = other.middleware.dup
129
139
  @query_analyzers = other.query_analyzers.dup
140
+ @multiplex_analyzers = other.multiplex_analyzers.dup
130
141
 
131
142
  @possible_types = GraphQL::Schema::PossibleTypes.new(self)
132
143
 
@@ -211,13 +222,36 @@ module GraphQL
211
222
  # Execute a query on itself. Raises an error if the schema definition is invalid.
212
223
  # @see {Query#initialize} for arguments.
213
224
  # @return [Hash] query result, ready to be serialized as JSON
214
- def execute(*args)
215
- if @definition_error
216
- raise @definition_error
217
- else
218
- query_obj = GraphQL::Query.new(self, *args)
219
- query_obj.result
225
+ def execute(query_str = nil, **kwargs)
226
+ if query_str
227
+ kwargs[:query] = query_str
220
228
  end
229
+ all_results = multiplex([kwargs])
230
+ all_results[0]
231
+ end
232
+
233
+ # Execute several queries on itself. Raises an error if the schema definition is invalid.
234
+ # @example Run several queries at once
235
+ # context = { ... }
236
+ # queries = [
237
+ # { query: params[:query_1], variables: params[:variables_1], context: context },
238
+ # { query: params[:query_2], variables: params[:variables_2], context: context },
239
+ # ]
240
+ # results = MySchema.multiplex(queries)
241
+ # render json: {
242
+ # result_1: results[0],
243
+ # result_2: results[1],
244
+ # }
245
+ #
246
+ # @see {Query#initialize} for query keyword arguments
247
+ # @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
248
+ # @param queries [Array<Hash>] Keyword arguments for each query
249
+ # @param context [Hash] Multiplex-level context
250
+ # @return [Array<Hash>] One result for each query in the input
251
+ def multiplex(*args)
252
+ with_definition_error_check {
253
+ GraphQL::Execution::Multiplex.run_all(self, *args)
254
+ }
221
255
  end
222
256
 
223
257
  # Resolve field named `field_name` for type `parent_type`.
@@ -225,17 +259,19 @@ module GraphQL
225
259
  # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
226
260
  # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`
227
261
  def get_field(parent_type, field_name)
228
- defined_field = @instrumented_field_map.get(parent_type.name, field_name)
229
- if defined_field
230
- defined_field
231
- elsif field_name == "__typename"
232
- GraphQL::Introspection::TypenameField
233
- elsif field_name == "__schema" && parent_type == query
234
- GraphQL::Introspection::SchemaField
235
- elsif field_name == "__type" && parent_type == query
236
- GraphQL::Introspection::TypeByNameField
237
- else
238
- nil
262
+ with_definition_error_check do
263
+ defined_field = @instrumented_field_map.get(parent_type.name, field_name)
264
+ if defined_field
265
+ defined_field
266
+ elsif field_name == "__typename"
267
+ GraphQL::Introspection::TypenameField
268
+ elsif field_name == "__schema" && parent_type == query
269
+ GraphQL::Introspection::SchemaField
270
+ elsif field_name == "__type" && parent_type == query
271
+ GraphQL::Introspection::TypeByNameField
272
+ else
273
+ nil
274
+ end
239
275
  end
240
276
  end
241
277
 
@@ -419,14 +455,31 @@ module GraphQL
419
455
  !!lazy_method_name(obj)
420
456
  end
421
457
 
422
- # Return a GraphQL schema string for the defined types in the schema
458
+ # Return the GraphQL IDL for the schema
423
459
  # @param context [Hash]
424
460
  # @param only [<#call(member, ctx)>]
425
461
  # @param except [<#call(member, ctx)>]
462
+ # @return [String]
426
463
  def to_definition(only: nil, except: nil, context: {})
427
464
  GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
428
465
  end
429
466
 
467
+ # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
468
+ # @param context [Hash]
469
+ # @param only [<#call(member, ctx)>]
470
+ # @param except [<#call(member, ctx)>]
471
+ # @return [Hash] GraphQL result
472
+ def as_json(only: nil, except: nil, context: {})
473
+ execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context)
474
+ end
475
+
476
+ # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
477
+ # @see {#as_json}
478
+ # @return [String]
479
+ def to_json(*args)
480
+ JSON.pretty_generate(as_json(*args))
481
+ end
482
+
430
483
  protected
431
484
 
432
485
  def rescues?
@@ -441,14 +494,18 @@ module GraphQL
441
494
 
442
495
  private
443
496
 
497
+ # Wrap Relay-related objects in wrappers
498
+ # @api private
499
+ BUILT_IN_INSTRUMENTERS = [
500
+ GraphQL::Relay::ConnectionInstrumentation,
501
+ GraphQL::Relay::Mutation::Instrumentation,
502
+ ]
503
+
444
504
  # Apply instrumentation to fields. Relay instrumentation is applied last
445
505
  # so that user-provided instrumentation can wrap user-provided resolve functions,
446
506
  # _then_ Relay helpers can wrap the returned objects.
447
507
  def build_instrumented_field_map
448
- all_instrumenters = @instrumenters[:field] + [
449
- GraphQL::Relay::ConnectionInstrumentation,
450
- GraphQL::Relay::Mutation::Instrumentation,
451
- ]
508
+ all_instrumenters = @instrumenters[:field] + BUILT_IN_INSTRUMENTERS + @instrumenters[:field_after_built_ins]
452
509
  @instrumented_field_map = InstrumentedFieldMap.new(self, all_instrumenters)
453
510
  end
454
511
 
@@ -456,5 +513,13 @@ module GraphQL
456
513
  all_types = orphan_types + [query, mutation, subscription, GraphQL::Introspection::SchemaType]
457
514
  @types = GraphQL::Schema::ReduceTypes.reduce(all_types.compact)
458
515
  end
516
+
517
+ def with_definition_error_check
518
+ if @definition_error
519
+ raise @definition_error
520
+ else
521
+ yield
522
+ end
523
+ end
459
524
  end
460
525
  end