graphql 2.5.18 → 2.5.20

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  4. data/lib/graphql/dashboard/application_controller.rb +41 -0
  5. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  6. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  7. data/lib/graphql/dashboard/subscriptions.rb +2 -1
  8. data/lib/graphql/dashboard.rb +9 -74
  9. data/lib/graphql/dataloader/null_dataloader.rb +7 -3
  10. data/lib/graphql/execution/batching/field_compatibility.rb +150 -0
  11. data/lib/graphql/execution/batching/field_resolve_step.rb +408 -0
  12. data/lib/graphql/execution/batching/prepare_object_step.rb +112 -0
  13. data/lib/graphql/execution/batching/runner.rb +352 -0
  14. data/lib/graphql/execution/batching/selections_step.rb +37 -0
  15. data/lib/graphql/execution/batching.rb +62 -0
  16. data/lib/graphql/execution_error.rb +13 -10
  17. data/lib/graphql/introspection/directive_type.rb +7 -3
  18. data/lib/graphql/introspection/entry_points.rb +6 -2
  19. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  20. data/lib/graphql/introspection/field_type.rb +13 -5
  21. data/lib/graphql/introspection/input_value_type.rb +21 -13
  22. data/lib/graphql/introspection/type_type.rb +64 -28
  23. data/lib/graphql/invalid_null_error.rb +11 -5
  24. data/lib/graphql/query/context.rb +3 -2
  25. data/lib/graphql/query/null_context.rb +9 -3
  26. data/lib/graphql/schema/argument.rb +4 -0
  27. data/lib/graphql/schema/build_from_definition.rb +26 -6
  28. data/lib/graphql/schema/field.rb +76 -1
  29. data/lib/graphql/schema/member/has_dataloader.rb +9 -0
  30. data/lib/graphql/schema/member/has_fields.rb +3 -0
  31. data/lib/graphql/schema/resolver.rb +3 -1
  32. data/lib/graphql/schema/visibility.rb +1 -1
  33. data/lib/graphql/schema.rb +33 -7
  34. data/lib/graphql/subscriptions.rb +1 -1
  35. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  36. data/lib/graphql/tracing/detailed_trace.rb +20 -5
  37. data/lib/graphql/tracing/perfetto_trace.rb +48 -2
  38. data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
  39. data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
  40. data/lib/graphql/types/relay/has_node_field.rb +5 -8
  41. data/lib/graphql/types/relay/has_nodes_field.rb +5 -8
  42. data/lib/graphql/unauthorized_error.rb +5 -1
  43. data/lib/graphql/version.rb +1 -1
  44. data/lib/graphql.rb +3 -0
  45. metadata +14 -2
@@ -0,0 +1,352 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ module Batching
5
+ class Runner
6
+ def initialize(multiplex)
7
+ @multiplex = multiplex
8
+ @schema = multiplex.schema
9
+ @steps_queue = []
10
+ @runtime_types_at_result = {}.compare_by_identity
11
+ @static_types_at_result = {}.compare_by_identity
12
+ @selected_operation = nil
13
+ @dataloader = multiplex.context[:dataloader] ||= @schema.dataloader_class.new
14
+ @resolves_lazies = @schema.resolves_lazies?
15
+ @field_resolve_step_class = @schema.uses_raw_value? ? RawValueFieldResolveStep : FieldResolveStep
16
+ @authorizes = {}.compare_by_identity
17
+ end
18
+
19
+ def add_step(step)
20
+ @dataloader.append_job(step)
21
+ end
22
+
23
+ attr_reader :steps_queue, :schema, :variables, :static_types_at_result, :runtime_types_at_result, :dataloader, :resolves_lazies, :authorizes
24
+
25
+ def execute
26
+ Fiber[:__graphql_current_multiplex] = @multiplex
27
+ isolated_steps = [[]]
28
+ trace = @multiplex.current_trace
29
+ queries = @multiplex.queries
30
+ multiplex_analyzers = @schema.multiplex_analyzers
31
+ if @multiplex.max_complexity
32
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
33
+ end
34
+
35
+ trace.execute_multiplex(multiplex: @multiplex) do
36
+ trace.begin_analyze_multiplex(@multiplex, multiplex_analyzers)
37
+ @schema.analysis_engine.analyze_multiplex(@multiplex, multiplex_analyzers)
38
+ trace.end_analyze_multiplex(@multiplex, multiplex_analyzers)
39
+
40
+ results = []
41
+ queries.each do |query|
42
+ if query.validate && !query.valid?
43
+ results << {
44
+ "errors" => query.static_errors.map(&:to_h)
45
+ }
46
+ next
47
+ end
48
+
49
+ selected_operation = query.document.definitions.first # TODO select named operation
50
+ data = {}
51
+
52
+ root_type = case selected_operation.operation_type
53
+ when nil, "query"
54
+ @schema.query
55
+ when "mutation"
56
+ @schema.mutation
57
+ when "subscription"
58
+ @schema.subscription
59
+ else
60
+ raise ArgumentError, "Unknown operation type: #{selected_operation.operation_type.inspect}"
61
+ end
62
+
63
+ auth_check = schema.sync_lazy(root_type.authorized?(query.root_value, query.context))
64
+ root_value = if auth_check
65
+ query.root_value
66
+ else
67
+ begin
68
+ auth_err = GraphQL::UnauthorizedError.new(object: query.root_value, type: root_type, context: query.context)
69
+ new_val = schema.unauthorized_object(auth_err)
70
+ if new_val
71
+ auth_check = true
72
+ end
73
+ new_val
74
+ rescue GraphQL::ExecutionError => ex_err
75
+ # The old runtime didn't add path and ast_nodes to this
76
+ query.context.add_error(ex_err)
77
+ nil
78
+ end
79
+ end
80
+
81
+ if !auth_check
82
+ results << {}
83
+ next
84
+ end
85
+
86
+ results << { "data" => data }
87
+
88
+ case selected_operation.operation_type
89
+ when nil, "query"
90
+ isolated_steps[0] << SelectionsStep.new(
91
+ parent_type: root_type,
92
+ selections: selected_operation.selections,
93
+ objects: [root_value],
94
+ results: [data],
95
+ path: EmptyObjects::EMPTY_ARRAY,
96
+ runner: self,
97
+ query: query,
98
+ )
99
+ when "mutation"
100
+ fields = {}
101
+ gather_selections(root_type, selected_operation.selections, nil, query, {}, into: fields)
102
+ fields.each_value do |field_resolve_step|
103
+ isolated_steps << [SelectionsStep.new(
104
+ parent_type: root_type,
105
+ selections: field_resolve_step.ast_nodes || Array(field_resolve_step.ast_node),
106
+ objects: [root_value],
107
+ results: [data],
108
+ path: EmptyObjects::EMPTY_ARRAY,
109
+ runner: self,
110
+ query: query,
111
+ )]
112
+ end
113
+ when "subscription"
114
+ raise ArgumentError, "TODO implement subscriptions"
115
+ else
116
+ raise ArgumentError, "Unhandled operation type: #{operation.operation_type.inspect}"
117
+ end
118
+
119
+ @static_types_at_result[data] = root_type
120
+ @runtime_types_at_result[data] = root_type
121
+
122
+ # TODO This is stupid but makes multiplex_spec.rb pass
123
+ trace.execute_query(query: query) do
124
+ end
125
+ end
126
+
127
+ while (next_isolated_steps = isolated_steps.shift)
128
+ next_isolated_steps.each do |step|
129
+ add_step(step)
130
+ end
131
+ @dataloader.run
132
+ end
133
+
134
+ # TODO This is stupid but makes multiplex_spec.rb pass
135
+ trace.execute_query_lazy(query: nil, multiplex: @multiplex) do
136
+ end
137
+
138
+ queries.each_with_index.map do |query, idx|
139
+ result = results[idx]
140
+ fin_result = if query.context.errors.empty?
141
+ result
142
+ else
143
+ data = result["data"]
144
+ data = propagate_errors(data, query)
145
+ errors = []
146
+ query.context.errors.each do |err|
147
+ if err.respond_to?(:to_h)
148
+ errors << err.to_h
149
+ end
150
+ end
151
+ res_h = {}
152
+ if !errors.empty?
153
+ res_h["errors"] = errors
154
+ end
155
+ res_h["data"] = data
156
+ res_h
157
+ end
158
+
159
+ GraphQL::Query::Result.new(query: query, values: fin_result)
160
+ end
161
+ end
162
+ ensure
163
+ Fiber[:__graphql_current_multiplex] = nil
164
+ end
165
+
166
+ def gather_selections(type_defn, ast_selections, selections_step, query, prototype_result, into:)
167
+ ast_selections.each do |ast_selection|
168
+ next if !directives_include?(query, ast_selection)
169
+ case ast_selection
170
+ when GraphQL::Language::Nodes::Field
171
+ key = ast_selection.alias || ast_selection.name
172
+ step = into[key] ||= begin
173
+ prototype_result[key] = nil
174
+
175
+ @field_resolve_step_class.new(
176
+ selections_step: selections_step,
177
+ key: key,
178
+ parent_type: type_defn,
179
+ runner: self,
180
+ )
181
+ end
182
+ step.append_selection(ast_selection)
183
+ when GraphQL::Language::Nodes::InlineFragment
184
+ type_condition = ast_selection.type&.name
185
+ if type_condition.nil? || type_condition_applies?(query.context, type_defn, type_condition)
186
+ gather_selections(type_defn, ast_selection.selections, selections_step, query, prototype_result, into: into)
187
+ end
188
+ when GraphQL::Language::Nodes::FragmentSpread
189
+ fragment_definition = query.document.definitions.find { |defn| defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && defn.name == ast_selection.name }
190
+ type_condition = fragment_definition.type.name
191
+ if type_condition_applies?(query.context, type_defn, type_condition)
192
+ gather_selections(type_defn, fragment_definition.selections, selections_step, query, prototype_result, into: into)
193
+ end
194
+ else
195
+ raise ArgumentError, "Unsupported graphql selection node: #{ast_selection.class} (#{ast_selection.inspect})"
196
+ end
197
+ end
198
+ end
199
+
200
+ private
201
+
202
+ def propagate_errors(data, query)
203
+ paths_to_check = query.context.errors.map(&:path)
204
+ paths_to_check.compact! # root-level auth errors currently come without a path
205
+ # TODO dry with above?
206
+ # This is also where a query-level "Step" would be used?
207
+ selected_operation = query.document.definitions.first # TODO pick a selected operation
208
+ root_type = case selected_operation.operation_type
209
+ when nil, "query"
210
+ query.schema.query
211
+ when "mutation"
212
+ query.schema.mutation
213
+ when "subscription"
214
+ raise "Not implemented yet, TODO"
215
+ end
216
+ check_object_result(query, data, root_type, selected_operation.selections, [], [], paths_to_check)
217
+ end
218
+
219
+ def check_object_result(query, result_h, static_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
220
+ current_path_len = current_exec_path.length
221
+ ast_selections.each do |ast_selection|
222
+ case ast_selection
223
+ when Language::Nodes::Field
224
+ begin
225
+ key = ast_selection.alias || ast_selection.name
226
+ current_exec_path << key
227
+ current_result_path << key
228
+ if paths_to_check.any? { |path_to_check| path_to_check[current_path_len] == key }
229
+ result_value = result_h[key]
230
+ field_defn = query.context.types.field(static_type, ast_selection.name)
231
+ result_type = field_defn.type
232
+ if (result_type_non_null = result_type.non_null?)
233
+ result_type = result_type.of_type
234
+ end
235
+ new_result_value = if result_value.is_a?(GraphQL::Error)
236
+ result_value.path = current_result_path.dup
237
+ nil
238
+ else
239
+ if result_type.list?
240
+ check_list_result(query, result_value, result_type.of_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
241
+ elsif result_type.kind.leaf?
242
+ result_value
243
+ else
244
+ check_object_result(query, result_value, result_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
245
+ end
246
+ end
247
+
248
+ if new_result_value.nil? && result_type_non_null
249
+ return nil
250
+ else
251
+ result_h[key] = new_result_value
252
+ end
253
+ end
254
+ ensure
255
+ current_exec_path.pop
256
+ current_result_path.pop
257
+ end
258
+ when Language::Nodes::InlineFragment
259
+ static_type_at_result = @static_types_at_result[result_h]
260
+ if static_type_at_result && type_condition_applies?(query.context, static_type_at_result, ast_selection.type.name)
261
+ result_h = check_object_result(query, result_h, static_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
262
+ end
263
+ when Language::Nodes::FragmentSpread
264
+ fragment_defn = query.document.definitions.find { |defn| defn.is_a?(Language::Nodes::FragmentDefinition) && defn.name == ast_selection.name }
265
+ static_type_at_result = @static_types_at_result[result_h]
266
+ if static_type_at_result && type_condition_applies?(query.context, static_type_at_result, fragment_defn.type.name)
267
+ result_h = check_object_result(query, result_h, static_type, fragment_defn.selections, current_exec_path, current_result_path, paths_to_check)
268
+ end
269
+ end
270
+ end
271
+
272
+ result_h
273
+ end
274
+
275
+ def check_list_result(query, result_arr, inner_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
276
+ inner_type_non_null = false
277
+ if inner_type.non_null?
278
+ inner_type_non_null = true
279
+ inner_type = inner_type.of_type
280
+ end
281
+
282
+ new_invalid_null = false
283
+ result_arr.map!.with_index do |result_item, idx|
284
+ current_result_path << idx
285
+ new_result = if result_item.is_a?(GraphQL::Error)
286
+ result_item.path = current_result_path.dup
287
+ nil
288
+ elsif inner_type.list?
289
+ check_list_result(query, result_item, inner_type.of_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
290
+ elsif inner_type.kind.leaf?
291
+ result_item
292
+ else
293
+ check_object_result(query, result_item, inner_type, ast_selections, current_exec_path, current_result_path, paths_to_check)
294
+ end
295
+
296
+ if new_result.nil? && inner_type_non_null
297
+ new_invalid_null = true
298
+ nil
299
+ else
300
+ new_result
301
+ end
302
+ ensure
303
+ current_result_path.pop
304
+ end
305
+
306
+ if new_invalid_null
307
+ nil
308
+ else
309
+ result_arr
310
+ end
311
+ end
312
+
313
+ def dir_arg_value(query, arg_node)
314
+ if arg_node.value.is_a?(Language::Nodes::VariableIdentifier)
315
+ var_key = arg_node.value.name
316
+ if query.variables.key?(var_key)
317
+ query.variables[var_key]
318
+ else
319
+ query.variables[var_key.to_sym]
320
+ end
321
+ else
322
+ arg_node.value
323
+ end
324
+ end
325
+ def directives_include?(query, ast_selection)
326
+ if ast_selection.directives.any? { |dir_node|
327
+ if dir_node.name == "skip"
328
+ dir_node.arguments.any? { |arg_node| arg_node.name == "if" && dir_arg_value(query, arg_node) == true } # rubocop:disable Development/ContextIsPassedCop
329
+ elsif dir_node.name == "include"
330
+ dir_node.arguments.any? { |arg_node| arg_node.name == "if" && dir_arg_value(query, arg_node) == false } # rubocop:disable Development/ContextIsPassedCop
331
+ end
332
+ }
333
+ false
334
+ else
335
+ true
336
+ end
337
+ end
338
+
339
+ def type_condition_applies?(context, concrete_type, type_name)
340
+ if type_name == concrete_type.graphql_name
341
+ true
342
+ else
343
+ abs_t = @schema.get_type(type_name, context)
344
+ p_types = @schema.possible_types(abs_t, context)
345
+ c_p_types = @schema.possible_types(concrete_type, context)
346
+ p_types.any? { |t| c_p_types.include?(t) }
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ module Batching
5
+ class SelectionsStep
6
+ def initialize(parent_type:, selections:, objects:, results:, runner:, query:, path:)
7
+ @path = path
8
+ @parent_type = parent_type
9
+ @selections = selections
10
+ @runner = runner
11
+ @objects = objects
12
+ @results = results
13
+ @query = query
14
+ @graphql_objects = nil
15
+ end
16
+
17
+ attr_reader :path, :query, :objects, :results
18
+
19
+ def graphql_objects
20
+ @graphql_objects ||= @objects.map do |obj|
21
+ @parent_type.scoped_new(obj, @query.context)
22
+ end
23
+ end
24
+
25
+ def call
26
+ grouped_selections = {}
27
+ prototype_result = @results.first
28
+ @runner.gather_selections(@parent_type, @selections, self, self.query, prototype_result, into: grouped_selections)
29
+ @results.each { |r| r.replace(prototype_result) }
30
+ grouped_selections.each_value do |frs|
31
+ @runner.add_step(frs)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/execution/batching/prepare_object_step"
3
+ require "graphql/execution/batching/field_compatibility"
4
+ require "graphql/execution/batching/field_resolve_step"
5
+ require "graphql/execution/batching/runner"
6
+ require "graphql/execution/batching/selections_step"
7
+ module GraphQL
8
+ module Execution
9
+ module Batching
10
+ module SchemaExtension
11
+ def execute_batching(query_str = nil, context: nil, document: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
12
+ multiplex_context = if context
13
+ {
14
+ backtrace: context[:backtrace],
15
+ tracers: context[:tracers],
16
+ trace: context[:trace],
17
+ dataloader: context[:dataloader],
18
+ trace_mode: context[:trace_mode],
19
+ }
20
+ else
21
+ {}
22
+ end
23
+ query_opts = {
24
+ query: query_str,
25
+ document: document,
26
+ context: context,
27
+ validate: validate,
28
+ variables: variables,
29
+ root_value: root_value,
30
+ visibility_profile: visibility_profile,
31
+ }
32
+ m_results = multiplex_batching([query_opts], context: multiplex_context, max_complexity: nil)
33
+ m_results[0]
34
+ end
35
+
36
+ def multiplex_batching(query_options, context: {}, max_complexity: self.max_complexity)
37
+ Batching.run_all(self, query_options, context: context, max_complexity: max_complexity)
38
+ end
39
+ end
40
+
41
+ def self.use(schema)
42
+ schema.extend(SchemaExtension)
43
+ end
44
+
45
+ def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
46
+ queries = query_options.map do |opts|
47
+ case opts
48
+ when Hash
49
+ schema.query_class.new(schema, nil, **opts)
50
+ when GraphQL::Query, GraphQL::Query::Partial
51
+ opts
52
+ else
53
+ raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
54
+ end
55
+ end
56
+ multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
57
+ runner = Runner.new(multiplex)
58
+ runner.execute
59
+ end
60
+ end
61
+ end
62
+ end
@@ -3,9 +3,17 @@ module GraphQL
3
3
  # If a field's resolve function returns a {ExecutionError},
4
4
  # the error will be inserted into the response's `"errors"` key
5
5
  # and the field will resolve to `nil`.
6
- class ExecutionError < GraphQL::Error
6
+ class ExecutionError < GraphQL::RuntimeError
7
7
  # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
8
- attr_accessor :ast_node
8
+ def ast_node
9
+ ast_nodes.first
10
+ end
11
+
12
+ def ast_node=(new_node)
13
+ @ast_nodes = [new_node]
14
+ end
15
+
16
+ attr_accessor :ast_nodes
9
17
 
10
18
  # @return [String] an array describing the JSON-path into the execution
11
19
  # response which corresponds to this error.
@@ -21,8 +29,8 @@ module GraphQL
21
29
  # under the `extensions` key.
22
30
  attr_accessor :extensions
23
31
 
24
- def initialize(message, ast_node: nil, options: nil, extensions: nil)
25
- @ast_node = ast_node
32
+ def initialize(message, ast_node: nil, ast_nodes: nil, options: nil, extensions: nil)
33
+ @ast_nodes = ast_nodes || [ast_node]
26
34
  @options = options
27
35
  @extensions = extensions
28
36
  super(message)
@@ -34,12 +42,7 @@ module GraphQL
34
42
  "message" => message,
35
43
  }
36
44
  if ast_node
37
- hash["locations"] = [
38
- {
39
- "line" => ast_node.line,
40
- "column" => ast_node.col,
41
- }
42
- ]
45
+ hash["locations"] = @ast_nodes.map { |a| { "line" => a.line, "column" => a.col } }
43
46
  end
44
47
  if path
45
48
  hash["path"] = path
@@ -12,7 +12,7 @@ module GraphQL
12
12
  field :name, String, null: false, method: :graphql_name
13
13
  field :description, String
14
14
  field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false
15
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
15
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false, resolve_each: :resolve_args do
16
16
  argument :include_deprecated, Boolean, required: false, default_value: false
17
17
  end
18
18
  field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
@@ -21,11 +21,15 @@ module GraphQL
21
21
 
22
22
  field :is_repeatable, Boolean, method: :repeatable?
23
23
 
24
- def args(include_deprecated:)
25
- args = @context.types.arguments(@object)
24
+ def self.resolve_args(object, context, include_deprecated:)
25
+ args = context.types.arguments(object)
26
26
  args = args.reject(&:deprecation_reason) unless include_deprecated
27
27
  args
28
28
  end
29
+
30
+ def args(include_deprecated:)
31
+ self.class.resolve_args(object, context, include_deprecated: include_deprecated)
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -2,18 +2,22 @@
2
2
  module GraphQL
3
3
  module Introspection
4
4
  class EntryPoints < Introspection::BaseObject
5
- field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true
5
+ field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true, resolve_static: :__schema
6
6
  field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do
7
7
  argument :name, String
8
8
  end
9
9
 
10
- def __schema
10
+ def self.__schema(context)
11
11
  # Apply wrapping manually since this field isn't wrapped by instrumentation
12
12
  schema = context.schema
13
13
  schema_type = schema.introspection_system.types["__Schema"]
14
14
  schema_type.wrap(schema, context)
15
15
  end
16
16
 
17
+ def __schema
18
+ self.class.__schema(context)
19
+ end
20
+
17
21
  def __type(name:)
18
22
  if context.types.reachable_type?(name) && (type = context.types.type(name))
19
23
  type
@@ -6,17 +6,17 @@ module GraphQL
6
6
  description "One possible value for a given Enum. Enum values are unique values, not a "\
7
7
  "placeholder for a string or numeric value. However an Enum value is returned in "\
8
8
  "a JSON response as a string."
9
- field :name, String, null: false
9
+ field :name, String, null: false, method: :graphql_name
10
10
  field :description, String
11
- field :is_deprecated, Boolean, null: false
11
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
12
12
  field :deprecation_reason, String
13
13
 
14
- def name
15
- object.graphql_name
14
+ def self.resolve_is_deprecated(object, context)
15
+ !!object.deprecation_reason
16
16
  end
17
17
 
18
18
  def is_deprecated
19
- !!@object.deprecation_reason
19
+ self.class.resolve_is_deprecated(object, context)
20
20
  end
21
21
  end
22
22
  end
@@ -7,22 +7,30 @@ module GraphQL
7
7
  "a name, potentially a list of arguments, and a return type."
8
8
  field :name, String, null: false
9
9
  field :description, String
10
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
10
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false, resolve_each: :resolve_args do
11
11
  argument :include_deprecated, Boolean, required: false, default_value: false
12
12
  end
13
13
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
14
- field :is_deprecated, Boolean, null: false
14
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
15
15
  field :deprecation_reason, String
16
16
 
17
+ def self.resolve_is_deprecated(object, _context)
18
+ !!object.deprecation_reason
19
+ end
20
+
17
21
  def is_deprecated
18
- !!@object.deprecation_reason
22
+ self.class.resolve_is_deprecated(object, context)
19
23
  end
20
24
 
21
- def args(include_deprecated:)
22
- args = @context.types.arguments(@object)
25
+ def self.resolve_args(object, context, include_deprecated:)
26
+ args = context.types.arguments(object)
23
27
  args = args.reject(&:deprecation_reason) unless include_deprecated
24
28
  args
25
29
  end
30
+
31
+ def args(include_deprecated:)
32
+ self.class.resolve_args(object, context, include_deprecated: include_deprecated)
33
+ end
26
34
  end
27
35
  end
28
36
  end