graphql 1.5.15 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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