graphql 2.5.20 → 2.5.21

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +3 -3
  3. data/lib/graphql/execution/multiplex.rb +1 -1
  4. data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
  5. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  6. data/lib/graphql/execution/{batching → next}/prepare_object_step.rb +26 -9
  7. data/lib/graphql/execution/{batching → next}/runner.rb +65 -28
  8. data/lib/graphql/execution/{batching → next}/selections_step.rb +1 -1
  9. data/lib/graphql/execution/{batching.rb → next.rb} +19 -12
  10. data/lib/graphql/execution.rb +1 -0
  11. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  12. data/lib/graphql/introspection/entry_points.rb +5 -1
  13. data/lib/graphql/pagination/connection.rb +2 -0
  14. data/lib/graphql/pagination/connections.rb +32 -0
  15. data/lib/graphql/schema/argument.rb +1 -0
  16. data/lib/graphql/schema/build_from_definition.rb +12 -25
  17. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  18. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  19. data/lib/graphql/schema/field.rb +52 -58
  20. data/lib/graphql/schema/field_extension.rb +33 -0
  21. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  22. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  23. data/lib/graphql/schema/member/has_dataloader.rb +8 -0
  24. data/lib/graphql/schema/member/has_fields.rb +5 -4
  25. data/lib/graphql/schema/member.rb +5 -0
  26. data/lib/graphql/schema/object.rb +1 -0
  27. data/lib/graphql/schema/resolver.rb +42 -0
  28. data/lib/graphql/types/relay/has_node_field.rb +10 -2
  29. data/lib/graphql/types/relay/has_nodes_field.rb +10 -2
  30. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  31. data/lib/graphql/version.rb +1 -1
  32. metadata +9 -8
  33. data/lib/graphql/execution/batching/field_compatibility.rb +0 -150
  34. data/lib/graphql/execution/batching/field_resolve_step.rb +0 -408
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ module Next
5
+ class LoadArgumentStep
6
+ def initialize(field_resolve_step:, arguments:, load_receiver:, argument_value:, argument_definition:, argument_key:)
7
+ @field_resolve_step = field_resolve_step
8
+ @load_receiver = load_receiver
9
+ @arguments = arguments
10
+ @argument_value = argument_value
11
+ @argument_definition = argument_definition
12
+ @argument_key = argument_key
13
+ @loaded_value = nil
14
+ end
15
+
16
+ def value
17
+ @loaded_value = @field_resolve_step.sync(@loaded_value)
18
+ assign_value
19
+ end
20
+
21
+ def call
22
+ context = @field_resolve_step.selections_step.query.context
23
+ @loaded_value = @load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
24
+ if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
25
+ runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
26
+ else
27
+ assign_value
28
+ end
29
+ rescue GraphQL::RuntimeError => err
30
+ @loaded_value = err
31
+ assign_value
32
+ rescue StandardError => stderr
33
+ @loaded_value = begin
34
+ context.query.handle_or_reraise(stderr)
35
+ rescue GraphQL::ExecutionError => ex_err
36
+ ex_err
37
+ end
38
+ assign_value
39
+ end
40
+
41
+ private
42
+
43
+ def assign_value
44
+ if @loaded_value.is_a?(GraphQL::Error)
45
+ @loaded_value.path = @field_resolve_step.path
46
+ @field_resolve_step.arguments = @loaded_value
47
+ else
48
+ @arguments[@argument_key] = @loaded_value
49
+ end
50
+
51
+ field_pending_steps = @field_resolve_step.pending_steps
52
+ field_pending_steps.delete(self)
53
+ if @field_resolve_step.arguments && field_pending_steps.size == 0 # rubocop:disable Development/ContextIsPassedCop
54
+ @field_resolve_step.runner.add_step(@field_resolve_step)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module Execution
4
- module Batching
4
+ module Next
5
5
  class PrepareObjectStep
6
6
  def initialize(static_type:, object:, runner:, graphql_result:, key:, is_non_null:, field_resolve_step:, next_objects:, next_results:, is_from_array:)
7
7
  @static_type = static_type
@@ -22,9 +22,15 @@ module GraphQL
22
22
 
23
23
  def value
24
24
  if @authorized_value
25
+ query = @field_resolve_step.selections_step.query
26
+ query.current_trace.begin_authorized(@resolved_type, @object, query.context)
25
27
  @authorized_value = @field_resolve_step.sync(@authorized_value)
28
+ query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
26
29
  elsif @resolved_type
30
+ ctx = @field_resolve_step.selections_step.query.context
31
+ ctx.query.current_trace.begin_resolve_type(@static_type, @object, ctx)
27
32
  @resolved_type, _ignored_value = @field_resolve_step.sync(@resolved_type)
33
+ ctx.query.current_trace.end_resolve_type(@static_type, @object, ctx, @resolved_type)
28
34
  end
29
35
  @runner.add_step(self)
30
36
  end
@@ -33,11 +39,14 @@ module GraphQL
33
39
  case @next_step
34
40
  when :resolve_type
35
41
  if @static_type.kind.abstract?
36
- @resolved_type, _ignored_value = @runner.schema.resolve_type(@static_type, @object, @field_resolve_step.selections_step.query.context)
42
+ ctx = @field_resolve_step.selections_step.query.context
43
+ ctx.query.current_trace.begin_resolve_type(@static_type, @object, ctx)
44
+ @resolved_type, _ignored_value = @runner.schema.resolve_type(@static_type, @object, ctx)
45
+ ctx.query.current_trace.end_resolve_type(@static_type, @object, ctx, @resolved_type)
37
46
  else
38
47
  @resolved_type = @static_type
39
48
  end
40
- if @runner.resolves_lazies && @runner.schema.lazy?(@resolved_type)
49
+ if @runner.resolves_lazies && @runner.lazy?(@resolved_type)
41
50
  @next_step = :authorize
42
51
  @runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
43
52
  else
@@ -53,14 +62,22 @@ module GraphQL
53
62
  end
54
63
 
55
64
  def authorize
56
- ctx = @field_resolve_step.selections_step.query.context
65
+ if @field_resolve_step.was_scoped && !@resolved_type.reauthorize_scoped_objects
66
+ @authorized_value = @object
67
+ create_result
68
+ return
69
+ end
70
+
71
+ query = @field_resolve_step.selections_step.query
57
72
  begin
58
- @authorized_value = @resolved_type.authorized?(@object, ctx)
73
+ query.current_trace.begin_authorized(@resolved_type, @object, query.context)
74
+ @authorized_value = @resolved_type.authorized?(@object, query.context)
75
+ query.current_trace.end_authorized(@resolve_type, @object, query.context, @authorized_value)
59
76
  rescue GraphQL::UnauthorizedError => auth_err
60
77
  @authorization_error = auth_err
61
78
  end
62
79
 
63
- if @runner.resolves_lazies && @runner.schema.lazy?(@authorized_value)
80
+ if @runner.resolves_lazies && @runner.lazy?(@authorized_value)
64
81
  @runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
65
82
  @next_step = :create_result
66
83
  else
@@ -100,11 +117,11 @@ module GraphQL
100
117
  @next_results << next_result_h
101
118
  @next_objects << @object
102
119
  @graphql_result[@key] = next_result_h
103
- @runner.runtime_types_at_result[next_result_h] = @resolved_type
104
- @runner.static_types_at_result[next_result_h] = @static_type
120
+ @runner.runtime_type_at[next_result_h] = @resolved_type
121
+ @runner.static_type_at[next_result_h] = @static_type
105
122
  end
106
123
 
107
- @field_resolve_step.authorized_finished
124
+ @field_resolve_step.authorized_finished(self)
108
125
  end
109
126
  end
110
127
  end
@@ -1,26 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module Execution
4
- module Batching
4
+ module Next
5
5
  class Runner
6
- def initialize(multiplex)
6
+ def initialize(multiplex, authorization:)
7
7
  @multiplex = multiplex
8
8
  @schema = multiplex.schema
9
9
  @steps_queue = []
10
- @runtime_types_at_result = {}.compare_by_identity
11
- @static_types_at_result = {}.compare_by_identity
10
+ @runtime_type_at = {}.compare_by_identity
11
+ @static_type_at = {}.compare_by_identity
12
12
  @selected_operation = nil
13
13
  @dataloader = multiplex.context[:dataloader] ||= @schema.dataloader_class.new
14
14
  @resolves_lazies = @schema.resolves_lazies?
15
+ @lazy_cache = resolves_lazies ? {}.compare_by_identity : nil
15
16
  @field_resolve_step_class = @schema.uses_raw_value? ? RawValueFieldResolveStep : FieldResolveStep
16
- @authorizes = {}.compare_by_identity
17
+ @authorization = authorization
18
+ if @authorization
19
+ @authorizes_cache = Hash.new do |h, query_context|
20
+ h[query_context] = {}.compare_by_identity
21
+ end.compare_by_identity
22
+ end
23
+ end
24
+
25
+ def resolve_type(type, object, query)
26
+ query.current_trace.begin_resolve_type(@static_type, object, query.context)
27
+ resolved_type, _ignored_new_value = query.resolve_type(type, object)
28
+ query.current_trace.end_resolve_type(@static_type, object, query.context, resolved_type)
29
+ resolved_type
30
+ end
31
+
32
+ def authorizes?(graphql_definition, query_context)
33
+ auth_cache = @authorizes_cache[query_context]
34
+ case (auth_res = auth_cache[graphql_definition])
35
+ when nil
36
+ auth_cache[graphql_definition] = graphql_definition.authorizes?(query_context)
37
+ else
38
+ auth_res
39
+ end
17
40
  end
18
41
 
19
42
  def add_step(step)
20
43
  @dataloader.append_job(step)
21
44
  end
22
45
 
23
- attr_reader :steps_queue, :schema, :variables, :static_types_at_result, :runtime_types_at_result, :dataloader, :resolves_lazies, :authorizes
46
+ attr_reader :authorization, :steps_queue, :schema, :variables, :dataloader, :resolves_lazies, :authorizes, :static_type_at, :runtime_type_at
24
47
 
25
48
  def execute
26
49
  Fiber[:__graphql_current_multiplex] = @multiplex
@@ -60,27 +83,33 @@ module GraphQL
60
83
  raise ArgumentError, "Unknown operation type: #{selected_operation.operation_type.inspect}"
61
84
  end
62
85
 
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
86
+ if self.authorization && authorizes?(root_type, query.context)
87
+ query.current_trace.begin_authorized(root_type, query.root_value, query.context)
88
+ auth_check = schema.sync_lazy(root_type.authorized?(query.root_value, query.context))
89
+ query.current_trace.end_authorized(root_type, query.root_value, query.context, auth_check)
90
+ root_value = if auth_check
91
+ query.root_value
92
+ else
93
+ begin
94
+ auth_err = GraphQL::UnauthorizedError.new(object: query.root_value, type: root_type, context: query.context)
95
+ new_val = schema.unauthorized_object(auth_err)
96
+ if new_val
97
+ auth_check = true
98
+ end
99
+ new_val
100
+ rescue GraphQL::ExecutionError => ex_err
101
+ # The old runtime didn't add path and ast_nodes to this
102
+ query.context.add_error(ex_err)
103
+ nil
72
104
  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
105
  end
79
- end
80
106
 
81
- if !auth_check
82
- results << {}
83
- next
107
+ if !auth_check
108
+ results << {}
109
+ next
110
+ end
111
+ else
112
+ root_value = query.root_value
84
113
  end
85
114
 
86
115
  results << { "data" => data }
@@ -116,8 +145,7 @@ module GraphQL
116
145
  raise ArgumentError, "Unhandled operation type: #{operation.operation_type.inspect}"
117
146
  end
118
147
 
119
- @static_types_at_result[data] = root_type
120
- @runtime_types_at_result[data] = root_type
148
+ @static_type_at[data] = root_type
121
149
 
122
150
  # TODO This is stupid but makes multiplex_spec.rb pass
123
151
  trace.execute_query(query: query) do
@@ -197,6 +225,15 @@ module GraphQL
197
225
  end
198
226
  end
199
227
 
228
+ def lazy?(object)
229
+ obj_class = object.class
230
+ is_lazy = @lazy_cache[obj_class]
231
+ if is_lazy.nil?
232
+ is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object)
233
+ end
234
+ is_lazy
235
+ end
236
+
200
237
  private
201
238
 
202
239
  def propagate_errors(data, query)
@@ -256,13 +293,13 @@ module GraphQL
256
293
  current_result_path.pop
257
294
  end
258
295
  when Language::Nodes::InlineFragment
259
- static_type_at_result = @static_types_at_result[result_h]
296
+ static_type_at_result = @static_type_at[result_h]
260
297
  if static_type_at_result && type_condition_applies?(query.context, static_type_at_result, ast_selection.type.name)
261
298
  result_h = check_object_result(query, result_h, static_type, ast_selection.selections, current_exec_path, current_result_path, paths_to_check)
262
299
  end
263
300
  when Language::Nodes::FragmentSpread
264
301
  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]
302
+ static_type_at_result = @static_type_at[result_h]
266
303
  if static_type_at_result && type_condition_applies?(query.context, static_type_at_result, fragment_defn.type.name)
267
304
  result_h = check_object_result(query, result_h, static_type, fragment_defn.selections, current_exec_path, current_result_path, paths_to_check)
268
305
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module Execution
4
- module Batching
4
+ module Next
5
5
  class SelectionsStep
6
6
  def initialize(parent_type:, selections:, objects:, results:, runner:, query:, path:)
7
7
  @path = path
@@ -1,14 +1,14 @@
1
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"
2
+ require "graphql/execution/next/prepare_object_step"
3
+ require "graphql/execution/next/field_resolve_step"
4
+ require "graphql/execution/next/load_argument_step"
5
+ require "graphql/execution/next/runner"
6
+ require "graphql/execution/next/selections_step"
7
7
  module GraphQL
8
8
  module Execution
9
- module Batching
9
+ module Next
10
10
  module SchemaExtension
11
- def execute_batching(query_str = nil, context: nil, document: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
11
+ def execute_next(query_str = nil, context: nil, document: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
12
12
  multiplex_context = if context
13
13
  {
14
14
  backtrace: context[:backtrace],
@@ -29,17 +29,24 @@ module GraphQL
29
29
  root_value: root_value,
30
30
  visibility_profile: visibility_profile,
31
31
  }
32
- m_results = multiplex_batching([query_opts], context: multiplex_context, max_complexity: nil)
32
+ m_results = multiplex_next([query_opts], context: multiplex_context, max_complexity: nil)
33
33
  m_results[0]
34
34
  end
35
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)
36
+ def multiplex_next(query_options, context: {}, max_complexity: self.max_complexity)
37
+ Next.run_all(self, query_options, context: context, max_complexity: max_complexity)
38
38
  end
39
+
40
+ def execution_next_options
41
+ @execution_next_options || find_inherited_value(:execution_next_options, EmptyObjects::EMPTY_HASH)
42
+ end
43
+
44
+ attr_writer :execution_next_options
39
45
  end
40
46
 
41
- def self.use(schema)
47
+ def self.use(schema, authorization: true)
42
48
  schema.extend(SchemaExtension)
49
+ schema.execution_next_options = { authorization: authorization }
43
50
  end
44
51
 
45
52
  def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
@@ -54,7 +61,7 @@ module GraphQL
54
61
  end
55
62
  end
56
63
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
57
- runner = Runner.new(multiplex)
64
+ runner = Runner.new(multiplex, **schema.execution_next_options)
58
65
  runner.execute
59
66
  end
60
67
  end
@@ -4,6 +4,7 @@ require "graphql/execution/interpreter"
4
4
  require "graphql/execution/lazy"
5
5
  require "graphql/execution/lookahead"
6
6
  require "graphql/execution/multiplex"
7
+ require "graphql/execution/next"
7
8
  require "graphql/execution/errors"
8
9
 
9
10
  module GraphQL
@@ -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)
@@ -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
@@ -519,35 +519,22 @@ module GraphQL
519
519
 
520
520
  # Don't do this for interfaces
521
521
  if default_resolve
522
- define_field_resolve_method(owner, resolve_method_name, field_definition.name, field_definition.arguments.empty?)
522
+ define_field_resolve_method(owner, resolve_method_name, field_definition.name)
523
523
  end
524
524
  end
525
525
  end
526
526
 
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
527
+ def define_field_resolve_method(owner, method_name, field_name)
528
+ owner.define_method(method_name) { |**args|
529
+ field_instance = context.types.field(owner, field_name)
530
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
531
+ }
532
+ owner.define_singleton_method(method_name) { |objects, context, **args|
533
+ field_instance = context.types.field(owner, field_name)
534
+ objects.map do |object|
535
+ context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
536
+ end
537
+ }
551
538
  end
552
539
 
553
540
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
@@ -21,45 +21,25 @@ module GraphQL
21
21
  yield(object, next_args, arguments)
22
22
  end
23
23
 
24
+ def resolve_next(objects:, arguments:, context:)
25
+ next_args = arguments.dup
26
+ next_args.delete(:first)
27
+ next_args.delete(:last)
28
+ next_args.delete(:before)
29
+ next_args.delete(:after)
30
+ yield(objects, next_args, arguments)
31
+ end
32
+
24
33
  def after_resolve(value:, object:, arguments:, context:, memo:)
25
34
  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
35
+ context.query.after_lazy(value) do |resolved_value|
36
+ context.schema.connections.populate_connection(field, object.object, resolved_value, original_arguments, context)
61
37
  end
62
38
  end
39
+
40
+ def after_resolve_next(**kwargs)
41
+ raise "This should never be called -- it's hardcoded in execution instead."
42
+ end
63
43
  end
64
44
  end
65
45
  end
@@ -5,24 +5,33 @@ 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
31
+
32
+ def after_resolve_next(**kwargs)
33
+ raise "This should never be called -- it's hardcoded in execution instead."
34
+ end
26
35
  end
27
36
  end
28
37
  end