graphql 2.5.20 → 2.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis.rb +20 -13
  3. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +3 -3
  4. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  5. data/lib/graphql/execution/finalize.rb +217 -0
  6. data/lib/graphql/execution/input_values.rb +261 -0
  7. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  8. data/lib/graphql/execution/interpreter/runtime.rb +3 -2
  9. data/lib/graphql/execution/interpreter.rb +6 -9
  10. data/lib/graphql/execution/lazy.rb +1 -1
  11. data/lib/graphql/execution/load_argument_step.rb +64 -0
  12. data/lib/graphql/execution/multiplex.rb +1 -1
  13. data/lib/graphql/execution/next.rb +90 -0
  14. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  15. data/lib/graphql/execution/runner.rb +410 -0
  16. data/lib/graphql/execution/selections_step.rb +91 -0
  17. data/lib/graphql/execution.rb +8 -4
  18. data/lib/graphql/execution_error.rb +5 -1
  19. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  20. data/lib/graphql/introspection/entry_points.rb +5 -1
  21. data/lib/graphql/pagination/connection.rb +2 -0
  22. data/lib/graphql/pagination/connections.rb +32 -0
  23. data/lib/graphql/query/context.rb +7 -1
  24. data/lib/graphql/query/partial.rb +18 -3
  25. data/lib/graphql/query.rb +10 -1
  26. data/lib/graphql/runtime_error.rb +6 -0
  27. data/lib/graphql/schema/argument.rb +1 -0
  28. data/lib/graphql/schema/build_from_definition.rb +22 -25
  29. data/lib/graphql/schema/directive.rb +23 -9
  30. data/lib/graphql/schema/field/connection_extension.rb +4 -37
  31. data/lib/graphql/schema/field/scope_extension.rb +18 -13
  32. data/lib/graphql/schema/field.rb +59 -62
  33. data/lib/graphql/schema/field_extension.rb +11 -8
  34. data/lib/graphql/schema/interface.rb +26 -0
  35. data/lib/graphql/schema/list.rb +5 -1
  36. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
  37. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  38. data/lib/graphql/schema/member/has_dataloader.rb +28 -0
  39. data/lib/graphql/schema/member/has_fields.rb +11 -5
  40. data/lib/graphql/schema/member.rb +5 -0
  41. data/lib/graphql/schema/non_null.rb +1 -1
  42. data/lib/graphql/schema/object.rb +1 -0
  43. data/lib/graphql/schema/resolver.rb +80 -0
  44. data/lib/graphql/schema/subscription.rb +0 -2
  45. data/lib/graphql/schema/visibility/profile.rb +68 -49
  46. data/lib/graphql/schema/wrapper.rb +7 -1
  47. data/lib/graphql/schema.rb +12 -10
  48. data/lib/graphql/static_validation/base_visitor.rb +90 -66
  49. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  50. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  51. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
  52. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
  53. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
  54. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -2
  55. data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
  56. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
  57. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  58. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
  59. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
  60. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
  61. data/lib/graphql/static_validation/validation_context.rb +1 -1
  62. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
  63. data/lib/graphql/subscriptions/event.rb +1 -0
  64. data/lib/graphql/subscriptions.rb +35 -0
  65. data/lib/graphql/tracing/perfetto_trace.rb +2 -2
  66. data/lib/graphql/tracing/trace.rb +6 -0
  67. data/lib/graphql/types/relay/has_node_field.rb +10 -2
  68. data/lib/graphql/types/relay/has_nodes_field.rb +10 -2
  69. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  70. data/lib/graphql/unauthorized_error.rb +4 -0
  71. data/lib/graphql/version.rb +1 -1
  72. data/lib/graphql.rb +1 -3
  73. metadata +13 -9
  74. data/lib/graphql/execution/batching/field_compatibility.rb +0 -150
  75. data/lib/graphql/execution/batching/field_resolve_step.rb +0 -408
  76. data/lib/graphql/execution/batching/prepare_object_step.rb +0 -112
  77. data/lib/graphql/execution/batching/runner.rb +0 -352
  78. data/lib/graphql/execution/batching/selections_step.rb +0 -37
  79. data/lib/graphql/execution/batching.rb +0 -62
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class SelectionsStep
5
+ def initialize(parent_type:, selections:, objects:, results:, runner:, query:, path:, clobber: true)
6
+ @path = path
7
+ @parent_type = parent_type
8
+ @selections = selections
9
+ @runner = runner
10
+ @objects = objects
11
+ @results = results
12
+ @query = query
13
+ @graphql_objects = nil
14
+ @all_selections = nil
15
+ @clobber = clobber
16
+ end
17
+
18
+ attr_reader :path, :query, :objects, :results
19
+
20
+ def graphql_objects
21
+ @graphql_objects ||= @objects.map do |obj|
22
+ @parent_type.scoped_new(obj, @query.context)
23
+ end
24
+ end
25
+
26
+ def call
27
+ @all_selections = [{}, (prototype_result = {})]
28
+ @runner.gather_selections(@parent_type, @selections, self, self.query, @all_selections, @all_selections[1], into: @all_selections[0])
29
+ continue_selections = []
30
+ i = 0
31
+ l = @all_selections.length
32
+ while i < l
33
+ grouped_selections = @all_selections[i]
34
+ selections_prototype_result = @all_selections[i + 1]
35
+ if (directives_owner = grouped_selections.delete(:__node))
36
+ directives = directives_owner.directives
37
+ continue_execution = true
38
+ directives.each do |dir_node|
39
+ dir_defn = @runner.runtime_directives[dir_node.name]
40
+ if dir_defn # not present for `skip` or `include`
41
+ dir_args = @runner.input_values[query].argument_values(dir_defn, dir_node.arguments, nil) # rubocop:disable Development/ContextIsPassedCop
42
+ result = case directives_owner
43
+ when Language::Nodes::FragmentSpread
44
+ dir_defn.resolve_fragment_spread(directives_owner, @parent_type, @objects, dir_args, self.query.context)
45
+ when Language::Nodes::InlineFragment
46
+ dir_defn.resolve_inline_fragment(directives_owner, @parent_type, @objects, dir_args, self.query.context)
47
+ else
48
+ raise ArgumentError, "Unhandled directive owner (#{directives_owner.class}): #{directives_owner.inspect}"
49
+ end
50
+ if result.is_a?(Finalizer)
51
+ result.path = path
52
+ @results.each do |r|
53
+ @runner.add_finalizer(@query, r, nil, result)
54
+ end
55
+ if result.is_a?(HaltExecution)
56
+ continue_execution = false
57
+ break
58
+ end
59
+ end
60
+
61
+ if continue_execution
62
+ prototype_result.merge!(selections_prototype_result)
63
+ grouped_selections.each_value { |v| continue_selections << v }
64
+ end
65
+ else
66
+ grouped_selections.each_value { |v| continue_selections << v }
67
+ end
68
+ end
69
+ else
70
+ grouped_selections.each_value { |v| continue_selections << v }
71
+ end
72
+
73
+ if @clobber
74
+ i2 = 0
75
+ l2 = @results.length
76
+ while i2 < l2
77
+ @results[i2].replace(prototype_result)
78
+ i2 += 1
79
+ end
80
+ end
81
+
82
+ continue_selections.each do |frs|
83
+ @runner.add_step(frs)
84
+ end
85
+
86
+ i += 2
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/execution/directive_checks"
3
+ require "graphql/execution/next"
3
4
  require "graphql/execution/interpreter"
4
5
  require "graphql/execution/lazy"
5
6
  require "graphql/execution/lookahead"
@@ -9,10 +10,13 @@ require "graphql/execution/errors"
9
10
  module GraphQL
10
11
  module Execution
11
12
  # @api private
12
- class Skip < GraphQL::Error; end
13
+ class Skip < GraphQL::RuntimeError
14
+ attr_accessor :path
15
+ def ast_nodes=(_ignored); end
13
16
 
14
- # Just a singleton for implementing {Query::Context#skip}
15
- # @api private
16
- SKIP = Skip.new
17
+ def finalize_graphql_result(query, result_data, key)
18
+ result_data.delete(key)
19
+ end
20
+ end
17
21
  end
18
22
  end
@@ -6,7 +6,7 @@ module GraphQL
6
6
  class ExecutionError < GraphQL::RuntimeError
7
7
  # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
8
8
  def ast_node
9
- ast_nodes.first
9
+ ast_nodes&.first
10
10
  end
11
11
 
12
12
  def ast_node=(new_node)
@@ -36,6 +36,10 @@ module GraphQL
36
36
  super(message)
37
37
  end
38
38
 
39
+ def finalize_graphql_result(query, result_data, key)
40
+ result_data[key] = nil
41
+ end
42
+
39
43
  # @return [Hash] An entry for the response's "errors" key
40
44
  def to_h
41
45
  hash = {
@@ -2,9 +2,13 @@
2
2
  module GraphQL
3
3
  module Introspection
4
4
  class DynamicFields < Introspection::BaseObject
5
- field :__typename, String, "The name of this type", null: false, dynamic_introspection: true
5
+ field :__typename, String, "The name of this type", null: false, dynamic_introspection: true, resolve_each: true
6
6
 
7
7
  def __typename
8
+ self.class.__typename(object, context)
9
+ end
10
+
11
+ def self.__typename(object, context)
8
12
  object.class.graphql_name
9
13
  end
10
14
  end
@@ -3,7 +3,7 @@ module GraphQL
3
3
  module Introspection
4
4
  class EntryPoints < Introspection::BaseObject
5
5
  field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true, resolve_static: :__schema
6
- field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do
6
+ field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true, resolve_static: :__type do
7
7
  argument :name, String
8
8
  end
9
9
 
@@ -19,6 +19,10 @@ module GraphQL
19
19
  end
20
20
 
21
21
  def __type(name:)
22
+ self.class.__type(context, name: name)
23
+ end
24
+
25
+ def self.__type(context, name:)
22
26
  if context.types.reachable_type?(name) && (type = context.types.type(name))
23
27
  type
24
28
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
@@ -94,6 +94,8 @@ module GraphQL
94
94
  @was_authorized_by_scope_items = detect_was_authorized_by_scope_items
95
95
  end
96
96
 
97
+ attr_writer :was_authorized_by_scope_items
98
+
97
99
  def was_authorized_by_scope_items?
98
100
  @was_authorized_by_scope_items
99
101
  end
@@ -83,6 +83,38 @@ module GraphQL
83
83
  end
84
84
  end
85
85
 
86
+ def populate_connection(field, object, value, original_arguments, context)
87
+ if value.is_a? GraphQL::ExecutionError
88
+ # This isn't even going to work because context doesn't have ast_node anymore
89
+ context.add_error(value)
90
+ nil
91
+ elsif value.nil?
92
+ nil
93
+ elsif value.is_a?(GraphQL::Pagination::Connection)
94
+ # update the connection with some things that may not have been provided
95
+ value.context ||= context
96
+ value.parent ||= object
97
+ value.first_value ||= original_arguments[:first]
98
+ value.after_value ||= original_arguments[:after]
99
+ value.last_value ||= original_arguments[:last]
100
+ value.before_value ||= original_arguments[:before]
101
+ value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
102
+ value.field ||= field
103
+ if field.has_max_page_size? && !value.has_max_page_size_override?
104
+ value.max_page_size = field.max_page_size
105
+ end
106
+ if field.has_default_page_size? && !value.has_default_page_size_override?
107
+ value.default_page_size = field.default_page_size
108
+ end
109
+ if (custom_t = context.schema.connections.edge_class_for_field(field))
110
+ value.edge_class = custom_t
111
+ end
112
+ value
113
+ else
114
+ context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
115
+ context.schema.connections.wrap(field, object, value, original_arguments, context)
116
+ end
117
+ end
86
118
  # use an override if there is one
87
119
  # @api private
88
120
  def edge_class_for_field(field)
@@ -112,7 +112,7 @@ module GraphQL
112
112
  # Return this value to tell the runtime
113
113
  # to exclude this field from the response altogether
114
114
  def skip
115
- GraphQL::Execution::SKIP
115
+ GraphQL::Execution::Skip.new
116
116
  end
117
117
 
118
118
  # Add error at query-level.
@@ -126,6 +126,12 @@ module GraphQL
126
126
  nil
127
127
  end
128
128
 
129
+ # @param value [Object] Any object to be inserted directly into the final response
130
+ # @return [GraphQL::Execution::Interpreter::RawValue] Return this from the field
131
+ def raw_value(value)
132
+ GraphQL::Execution::Interpreter::RawValue.new(value)
133
+ end
134
+
129
135
  # @example Print the GraphQL backtrace during field resolution
130
136
  # puts ctx.backtrace
131
137
  #
@@ -32,6 +32,7 @@ module GraphQL
32
32
  @multiplex = nil
33
33
  @result_values = nil
34
34
  @result = nil
35
+ @finalizers = @top_level_finalizers = nil
35
36
 
36
37
  if fragment_node
37
38
  @ast_nodes = [fragment_node]
@@ -51,6 +52,10 @@ module GraphQL
51
52
  @leaf
52
53
  end
53
54
 
55
+ def root_value
56
+ object
57
+ end
58
+
54
59
  attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :schema
55
60
 
56
61
  attr_accessor :multiplex, :result_values
@@ -90,10 +95,22 @@ module GraphQL
90
95
  @query.fragments
91
96
  end
92
97
 
98
+ def validate
99
+ @query.validate
100
+ end
101
+
93
102
  def valid?
94
103
  @query.valid?
95
104
  end
96
105
 
106
+ def query?
107
+ true
108
+ end
109
+
110
+ def run_partials(...)
111
+ @query.run_partials(...)
112
+ end
113
+
97
114
  def analyzers
98
115
  EmptyObjects::EMPTY_ARRAY
99
116
  end
@@ -107,7 +124,7 @@ module GraphQL
107
124
  end
108
125
 
109
126
  def selected_operation
110
- ast_nodes.first
127
+ Language::Nodes::OperationDefinition.new(selections: ast_nodes.flat_map(&:selections))
111
128
  end
112
129
 
113
130
  def static_errors
@@ -123,7 +140,6 @@ module GraphQL
123
140
  def set_type_info_from_path
124
141
  selections = [@query.selected_operation]
125
142
  type = @query.root_type
126
- parent_type = nil
127
143
  field_defn = nil
128
144
 
129
145
  @path.each do |name_in_doc|
@@ -162,7 +178,6 @@ module GraphQL
162
178
  end
163
179
  field_name = next_selections.first.name
164
180
  field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
165
- parent_type = type
166
181
  type = field_defn.type
167
182
  if type.non_null?
168
183
  type = type.of_type
data/lib/graphql/query.rb CHANGED
@@ -159,6 +159,7 @@ module GraphQL
159
159
  @root_value = root_value
160
160
  @fragments = nil
161
161
  @operations = nil
162
+ @finalizers = @top_level_finalizers = nil
162
163
  @validate = validate
163
164
  self.static_validator = static_validator if static_validator
164
165
  context_tracers = (context ? context.fetch(:tracers, []) : [])
@@ -262,6 +263,10 @@ module GraphQL
262
263
  with_prepared_ast { @operations }
263
264
  end
264
265
 
266
+ def path
267
+ EmptyObjects::EMPTY_ARRAY
268
+ end
269
+
265
270
  # Run subtree partials of this query and return their results.
266
271
  # Each partial is identified with a `path:` and `object:`
267
272
  # where the path references a field in the AST and the object will be treated
@@ -271,7 +276,11 @@ module GraphQL
271
276
  # @return [Array<GraphQL::Query::Result>]
272
277
  def run_partials(partials_hashes)
273
278
  partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
274
- Execution::Interpreter.run_all(@schema, partials, context: @context)
279
+ if context[:__graphql_execute_next]
280
+ Execution::Next.run_all(@schema, partials, context: @context)
281
+ else
282
+ Execution::Interpreter.run_all(@schema, partials, context: @context)
283
+ end
275
284
  end
276
285
 
277
286
  # Get the result for this query, executing it once
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class RuntimeError < Error
4
+ include GraphQL::Execution::Finalizer
5
+ end
6
+ end
@@ -4,6 +4,7 @@ module GraphQL
4
4
  class Argument
5
5
  include GraphQL::Schema::Member::HasPath
6
6
  include GraphQL::Schema::Member::HasAstNode
7
+ include GraphQL::Schema::Member::HasAuthorization
7
8
  include GraphQL::Schema::Member::HasDirectives
8
9
  include GraphQL::Schema::Member::HasDeprecationReason
9
10
  include GraphQL::Schema::Member::HasValidators
@@ -99,6 +99,16 @@ module GraphQL
99
99
  # It's possible that this was already loaded by the directives
100
100
  prev_type = types[definition.name]
101
101
  if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
102
+ if definition.is_a?(GraphQL::Language::Nodes::ObjectTypeDefinition) || definition.is_a?(Language::Nodes::InterfaceTypeDefinition)
103
+ interface_names = definition.interfaces.map(&:name)
104
+ transitive_names = interface_names.map { |n| document.definitions.find { |d| d.respond_to?(:name) && d.name == n }&.interfaces&.map(&:name) }
105
+ transitive_names.flatten!
106
+ transitive_names.compact!
107
+ if !(missing_transitive_interfaces = transitive_names - interface_names).empty?
108
+ raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again."
109
+ end
110
+ end
111
+
102
112
  types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types)
103
113
  end
104
114
  end
@@ -519,35 +529,22 @@ module GraphQL
519
529
 
520
530
  # Don't do this for interfaces
521
531
  if default_resolve
522
- define_field_resolve_method(owner, resolve_method_name, field_definition.name, field_definition.arguments.empty?)
532
+ define_field_resolve_method(owner, resolve_method_name, field_definition.name)
523
533
  end
524
534
  end
525
535
  end
526
536
 
527
- def define_field_resolve_method(owner, method_name, field_name, empty_arguments)
528
- if empty_arguments
529
- owner.define_method(method_name) {
530
- field_instance = context.types.field(owner, field_name)
531
- context.schema.definition_default_resolve.call(self.class, field_instance, object, EmptyObjects::EMPTY_HASH, context)
532
- }
533
- owner.define_singleton_method(method_name) { |objects, context|
534
- field_instance = context.types.field(owner, field_name)
535
- objects.map do |object|
536
- context.schema.definition_default_resolve.call(self, field_instance, object, EmptyObjects::EMPTY_HASH, context)
537
- end
538
- }
539
- else
540
- owner.define_method(method_name) { |**args|
541
- field_instance = context.types.field(owner, field_name)
542
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
543
- }
544
- owner.define_singleton_method(method_name) { |objects, context, **args|
545
- field_instance = context.types.field(owner, field_name)
546
- objects.map do |object|
547
- context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
548
- end
549
- }
550
- end
537
+ def define_field_resolve_method(owner, method_name, field_name)
538
+ owner.define_method(method_name) { |**args|
539
+ field_instance = context.types.field(owner, field_name)
540
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
541
+ }
542
+ owner.define_singleton_method(method_name) { |objects, context, **args|
543
+ field_instance = context.types.field(owner, field_name)
544
+ objects.map do |object|
545
+ context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
546
+ end
547
+ }
551
548
  end
552
549
 
553
550
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
@@ -31,17 +31,25 @@ module GraphQL
31
31
 
32
32
  def locations(*new_locations)
33
33
  if !new_locations.empty?
34
+ is_runtime = false
34
35
  new_locations.each do |new_loc|
35
- if !LOCATIONS.include?(new_loc.to_sym)
36
+ loc_sym = new_loc.to_sym
37
+ if !LOCATIONS.include?(loc_sym)
36
38
  raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
37
39
  end
40
+ is_runtime ||= RUNTIME_LOCATIONS.include?(loc_sym)
38
41
  end
39
42
  @locations = new_locations
43
+ @is_runtime = is_runtime
40
44
  else
41
45
  @locations ||= (superclass.respond_to?(:locations) ? superclass.locations : [])
42
46
  end
43
47
  end
44
48
 
49
+ def runtime?
50
+ @is_runtime
51
+ end
52
+
45
53
  def default_directive(new_default_directive = nil)
46
54
  if new_default_directive != nil
47
55
  @default_directive = new_default_directive
@@ -104,8 +112,12 @@ module GraphQL
104
112
 
105
113
  def inherited(subclass)
106
114
  super
115
+ parent_class = self
107
116
  subclass.class_exec do
108
117
  @default_graphql_name ||= nil
118
+ @locations = parent_class.locations
119
+ @is_runtime = parent_class.runtime?
120
+ @repeatable = false
109
121
  end
110
122
  end
111
123
  end
@@ -177,13 +189,16 @@ module GraphQL
177
189
  end
178
190
 
179
191
  LOCATIONS = [
180
- QUERY = :QUERY,
181
- MUTATION = :MUTATION,
182
- SUBSCRIPTION = :SUBSCRIPTION,
183
- FIELD = :FIELD,
184
- FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
185
- FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
186
- INLINE_FRAGMENT = :INLINE_FRAGMENT,
192
+ *(RUNTIME_LOCATIONS = [
193
+ QUERY = :QUERY,
194
+ MUTATION = :MUTATION,
195
+ SUBSCRIPTION = :SUBSCRIPTION,
196
+ FIELD = :FIELD,
197
+ FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
198
+ FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
199
+ INLINE_FRAGMENT = :INLINE_FRAGMENT,
200
+ VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
201
+ ]),
187
202
  SCHEMA = :SCHEMA,
188
203
  SCALAR = :SCALAR,
189
204
  OBJECT = :OBJECT,
@@ -195,7 +210,6 @@ module GraphQL
195
210
  ENUM_VALUE = :ENUM_VALUE,
196
211
  INPUT_OBJECT = :INPUT_OBJECT,
197
212
  INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
198
- VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
199
213
  ]
200
214
 
201
215
  DEFAULT_DEPRECATION_REASON = 'No longer supported'
@@ -12,52 +12,19 @@ module GraphQL
12
12
  end
13
13
 
14
14
  # Remove pagination args before passing it to a user method
15
- def resolve(object:, arguments:, context:)
15
+ def resolve(object: nil, objects: nil, arguments:, context:)
16
16
  next_args = arguments.dup
17
17
  next_args.delete(:first)
18
18
  next_args.delete(:last)
19
19
  next_args.delete(:before)
20
20
  next_args.delete(:after)
21
- yield(object, next_args, arguments)
21
+ yield(object || objects, next_args, arguments)
22
22
  end
23
23
 
24
24
  def after_resolve(value:, object:, arguments:, context:, memo:)
25
25
  original_arguments = memo
26
- # rename some inputs to avoid conflicts inside the block
27
- maybe_lazy = value
28
- value = nil
29
- context.query.after_lazy(maybe_lazy) do |resolved_value|
30
- value = resolved_value
31
- if value.is_a? GraphQL::ExecutionError
32
- # This isn't even going to work because context doesn't have ast_node anymore
33
- context.add_error(value)
34
- nil
35
- elsif value.nil?
36
- nil
37
- elsif value.is_a?(GraphQL::Pagination::Connection)
38
- # update the connection with some things that may not have been provided
39
- value.context ||= context
40
- value.parent ||= object.object
41
- value.first_value ||= original_arguments[:first]
42
- value.after_value ||= original_arguments[:after]
43
- value.last_value ||= original_arguments[:last]
44
- value.before_value ||= original_arguments[:before]
45
- value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
46
- value.field ||= field
47
- if field.has_max_page_size? && !value.has_max_page_size_override?
48
- value.max_page_size = field.max_page_size
49
- end
50
- if field.has_default_page_size? && !value.has_default_page_size_override?
51
- value.default_page_size = field.default_page_size
52
- end
53
- if (custom_t = context.schema.connections.edge_class_for_field(@field))
54
- value.edge_class = custom_t
55
- end
56
- value
57
- else
58
- context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
59
- context.schema.connections.wrap(field, object.object, value, original_arguments, context)
60
- end
26
+ context.query.after_lazy(value) do |resolved_value|
27
+ context.schema.connections.populate_connection(field, object.object, resolved_value, original_arguments, context)
61
28
  end
62
29
  end
63
30
  end
@@ -5,22 +5,27 @@ module GraphQL
5
5
  class Field
6
6
  class ScopeExtension < GraphQL::Schema::FieldExtension
7
7
  def after_resolve(object:, arguments:, context:, value:, memo:)
8
- if value.nil?
9
- value
10
- else
11
- ret_type = @field.type.unwrap
12
- if ret_type.respond_to?(:scope_items)
13
- scoped_items = ret_type.scope_items(value, context)
14
- if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
15
- if (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
16
- (query_runtime_state = current_runtime_state[context.query])
17
- query_runtime_state.was_authorized_by_scope_items = true
8
+ if object.is_a?(GraphQL::Schema::Object)
9
+ if value.nil?
10
+ value
11
+ else
12
+ return_type = field.type.unwrap
13
+ if return_type.respond_to?(:scope_items)
14
+ scoped_items = return_type.scope_items(value, context)
15
+ if !scoped_items.equal?(value) && !return_type.reauthorize_scoped_objects
16
+ if (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
17
+ (query_runtime_state = current_runtime_state[context.query])
18
+ query_runtime_state.was_authorized_by_scope_items = true
19
+ end
18
20
  end
21
+ scoped_items
22
+ else
23
+ value
19
24
  end
20
- scoped_items
21
- else
22
- value
23
25
  end
26
+ else
27
+ # TODO skip this entirely?
28
+ value
24
29
  end
25
30
  end
26
31
  end