graphql 2.5.19 → 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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/dashboard/application_controller.rb +41 -0
  3. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  4. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  5. data/lib/graphql/dashboard/subscriptions.rb +2 -1
  6. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +3 -3
  7. data/lib/graphql/dashboard.rb +9 -74
  8. data/lib/graphql/dataloader/null_dataloader.rb +7 -3
  9. data/lib/graphql/execution/multiplex.rb +1 -1
  10. data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
  11. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  12. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  13. data/lib/graphql/execution/next/runner.rb +389 -0
  14. data/lib/graphql/execution/next/selections_step.rb +37 -0
  15. data/lib/graphql/execution/next.rb +69 -0
  16. data/lib/graphql/execution.rb +1 -0
  17. data/lib/graphql/execution_error.rb +13 -10
  18. data/lib/graphql/introspection/directive_type.rb +7 -3
  19. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  20. data/lib/graphql/introspection/entry_points.rb +11 -3
  21. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  22. data/lib/graphql/introspection/field_type.rb +13 -5
  23. data/lib/graphql/introspection/input_value_type.rb +21 -13
  24. data/lib/graphql/introspection/type_type.rb +64 -28
  25. data/lib/graphql/invalid_null_error.rb +11 -5
  26. data/lib/graphql/pagination/connection.rb +2 -0
  27. data/lib/graphql/pagination/connections.rb +32 -0
  28. data/lib/graphql/query/context.rb +3 -2
  29. data/lib/graphql/query/null_context.rb +9 -3
  30. data/lib/graphql/schema/argument.rb +5 -0
  31. data/lib/graphql/schema/build_from_definition.rb +7 -0
  32. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  33. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  34. data/lib/graphql/schema/field.rb +70 -1
  35. data/lib/graphql/schema/field_extension.rb +33 -0
  36. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  37. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  38. data/lib/graphql/schema/member/has_dataloader.rb +17 -0
  39. data/lib/graphql/schema/member/has_fields.rb +5 -1
  40. data/lib/graphql/schema/member.rb +5 -0
  41. data/lib/graphql/schema/object.rb +1 -0
  42. data/lib/graphql/schema/resolver.rb +45 -1
  43. data/lib/graphql/schema/visibility.rb +1 -1
  44. data/lib/graphql/schema.rb +33 -7
  45. data/lib/graphql/subscriptions.rb +1 -1
  46. data/lib/graphql/tracing/perfetto_trace.rb +1 -1
  47. data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
  48. data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
  49. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  50. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  51. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  52. data/lib/graphql/unauthorized_error.rb +5 -1
  53. data/lib/graphql/version.rb +1 -1
  54. data/lib/graphql.rb +3 -0
  55. metadata +12 -2
@@ -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,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
@@ -2,19 +2,27 @@
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
6
- field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do
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, resolve_static: :__type 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:)
22
+ self.class.__type(context, name: name)
23
+ end
24
+
25
+ def self.__type(context, name:)
18
26
  if context.types.reachable_type?(name) && (type = context.types.type(name))
19
27
  type
20
28
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
@@ -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
@@ -9,53 +9,61 @@ module GraphQL
9
9
  field :name, String, null: false
10
10
  field :description, String
11
11
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
12
- field :default_value, String, "A GraphQL-formatted string representing the default value for this input value."
13
- field :is_deprecated, Boolean, null: false
12
+ field :default_value, String, "A GraphQL-formatted string representing the default value for this input value.", resolve_each: :resolve_default_value
13
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
14
14
  field :deprecation_reason, String
15
15
 
16
+ def self.resolve_is_deprecated(object, context)
17
+ !!object.deprecation_reason
18
+ end
19
+
16
20
  def is_deprecated
17
- !!@object.deprecation_reason
21
+ self.class.resolve_is_deprecated(object, context)
18
22
  end
19
23
 
20
- def default_value
21
- if @object.default_value?
22
- value = @object.default_value
24
+ def self.resolve_default_value(object, context)
25
+ if object.default_value?
26
+ value = object.default_value
23
27
  if value.nil?
24
28
  'null'
25
29
  else
26
- if (@object.type.kind.list? || (@object.type.kind.non_null? && @object.type.of_type.kind.list?)) && !value.respond_to?(:map)
30
+ if (object.type.kind.list? || (object.type.kind.non_null? && object.type.of_type.kind.list?)) && !value.respond_to?(:map)
27
31
  # This is a bit odd -- we expect the default value to be an application-style value, so we use coerce result below.
28
32
  # But coerce_result doesn't wrap single-item lists, which are valid inputs to list types.
29
33
  # So, apply that wrapper here if needed.
30
34
  value = [value]
31
35
  end
32
- coerced_default_value = @object.type.coerce_result(value, @context)
33
- serialize_default_value(coerced_default_value, @object.type)
36
+ coerced_default_value = object.type.coerce_result(value, context)
37
+ serialize_default_value(coerced_default_value, object.type, context)
34
38
  end
35
39
  else
36
40
  nil
37
41
  end
38
42
  end
39
43
 
44
+ def default_value
45
+ self.class.resolve_default_value(object, context)
46
+ end
47
+
40
48
 
41
49
  private
42
50
 
43
51
  # Recursively serialize, taking care not to add quotes to enum values
44
- def serialize_default_value(value, type)
52
+ def self.serialize_default_value(value, type, context)
45
53
  if value.nil?
46
54
  'null'
47
55
  elsif type.kind.list?
48
56
  inner_type = type.of_type
49
- "[" + value.map { |v| serialize_default_value(v, inner_type) }.join(", ") + "]"
57
+ "[" + value.map { |v| serialize_default_value(v, inner_type, context) }.join(", ") + "]"
50
58
  elsif type.kind.non_null?
51
- serialize_default_value(value, type.of_type)
59
+ serialize_default_value(value, type.of_type, context)
52
60
  elsif type.kind.enum?
53
61
  value
54
62
  elsif type.kind.input_object?
55
63
  "{" +
56
64
  value.map do |k, v|
57
65
  arg_defn = type.get_argument(k, context)
58
- "#{k}: #{serialize_default_value(v, arg_defn.type)}"
66
+ "#{k}: #{serialize_default_value(v, arg_defn.type, context)}"
59
67
  end.join(", ") +
60
68
  "}"
61
69
  else
@@ -11,32 +11,36 @@ module GraphQL
11
11
  "they describe. Abstract types, Union and Interface, provide the Object types "\
12
12
  "possible at runtime. List and NonNull types compose other types."
13
13
 
14
- field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false
14
+ field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false, resolve_each: :resolve_kind
15
15
  field :name, String, method: :graphql_name
16
16
  field :description, String
17
- field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false do
17
+ field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false, resolve_each: :resolve_fields do
18
18
  argument :include_deprecated, Boolean, required: false, default_value: false
19
19
  end
20
- field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
21
- field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
22
- field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false do
20
+ field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false, resolve_each: :resolve_interfaces
21
+ field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false, resolve_each: :resolve_possible_types
22
+ field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false, resolve_each: :resolve_enum_values do
23
23
  argument :include_deprecated, Boolean, required: false, default_value: false
24
24
  end
25
- field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false do
25
+ field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false, resolve_each: :resolve_input_fields do
26
26
  argument :include_deprecated, Boolean, required: false, default_value: false
27
27
  end
28
- field :of_type, GraphQL::Schema::LateBoundType.new("__Type")
28
+ field :of_type, GraphQL::Schema::LateBoundType.new("__Type"), resolve_each: :resolve_of_type
29
29
 
30
- field :specifiedByURL, String, resolver_method: :specified_by_url
30
+ field :specifiedByURL, String, resolve_each: :resolve_specified_by_url, resolver_method: :specified_by_url
31
31
 
32
- field :is_one_of, Boolean, null: false
32
+ field :is_one_of, Boolean, null: false, resolve_each: :resolve_is_one_of
33
33
 
34
- def is_one_of
34
+ def self.resolve_is_one_of(object, _ctx)
35
35
  object.kind.input_object? &&
36
36
  object.directives.any? { |d| d.graphql_name == "oneOf" }
37
37
  end
38
38
 
39
- def specified_by_url
39
+ def is_one_of
40
+ self.class.resolve_is_one_of(object, context)
41
+ end
42
+
43
+ def self.resolve_specified_by_url(object, _ctx)
40
44
  if object.kind.scalar?
41
45
  object.specified_by_url
42
46
  else
@@ -44,15 +48,23 @@ module GraphQL
44
48
  end
45
49
  end
46
50
 
51
+ def specified_by_url
52
+ self.class.resolve_specified_by_url(object, context)
53
+ end
54
+
55
+ def self.resolve_kind(object, context)
56
+ object.kind.name
57
+ end
58
+
47
59
  def kind
48
- @object.kind.name
60
+ self.class.resolve_kind(object, context)
49
61
  end
50
62
 
51
- def enum_values(include_deprecated:)
52
- if !@object.kind.enum?
63
+ def self.resolve_enum_values(object, context, include_deprecated:)
64
+ if !object.kind.enum?
53
65
  nil
54
66
  else
55
- enum_values = @context.types.enum_values(@object)
67
+ enum_values = context.types.enum_values(object)
56
68
 
57
69
  if !include_deprecated
58
70
  enum_values = enum_values.select {|f| !f.deprecation_reason }
@@ -62,17 +74,25 @@ module GraphQL
62
74
  end
63
75
  end
64
76
 
65
- def interfaces
66
- if @object.kind.object? || @object.kind.interface?
67
- @context.types.interfaces(@object).sort_by(&:graphql_name)
77
+ def enum_values(include_deprecated:)
78
+ self.class.resolve_enum_values(object, context, include_deprecated: include_deprecated)
79
+ end
80
+
81
+ def self.resolve_interfaces(object, context)
82
+ if object.kind.object? || object.kind.interface?
83
+ context.types.interfaces(object).sort_by(&:graphql_name)
68
84
  else
69
85
  nil
70
86
  end
71
87
  end
72
88
 
73
- def input_fields(include_deprecated:)
74
- if @object.kind.input_object?
75
- args = @context.types.arguments(@object)
89
+ def interfaces
90
+ self.class.resolve_interfaces(object, context)
91
+ end
92
+
93
+ def self.resolve_input_fields(object, context, include_deprecated:)
94
+ if object.kind.input_object?
95
+ args = context.types.arguments(object)
76
96
  args = args.reject(&:deprecation_reason) unless include_deprecated
77
97
  args
78
98
  else
@@ -80,19 +100,27 @@ module GraphQL
80
100
  end
81
101
  end
82
102
 
83
- def possible_types
84
- if @object.kind.abstract?
85
- @context.types.possible_types(@object).sort_by(&:graphql_name)
103
+ def input_fields(include_deprecated:)
104
+ self.class.resolve_input_fields(object, context, include_deprecated: include_deprecated)
105
+ end
106
+
107
+ def self.resolve_possible_types(object, context)
108
+ if object.kind.abstract?
109
+ context.types.possible_types(object).sort_by(&:graphql_name)
86
110
  else
87
111
  nil
88
112
  end
89
113
  end
90
114
 
91
- def fields(include_deprecated:)
92
- if !@object.kind.fields?
115
+ def possible_types
116
+ self.class.resolve_possible_types(object, context)
117
+ end
118
+
119
+ def self.resolve_fields(object, context, include_deprecated:)
120
+ if !object.kind.fields?
93
121
  nil
94
122
  else
95
- fields = @context.types.fields(@object)
123
+ fields = context.types.fields(object)
96
124
  if !include_deprecated
97
125
  fields = fields.select {|f| !f.deprecation_reason }
98
126
  end
@@ -100,8 +128,16 @@ module GraphQL
100
128
  end
101
129
  end
102
130
 
131
+ def fields(include_deprecated:)
132
+ self.class.resolve_fields(object, context, include_deprecated: include_deprecated)
133
+ end
134
+
135
+ def self.resolve_of_type(object, _ctx)
136
+ object.kind.wraps? ? object.of_type : nil
137
+ end
138
+
103
139
  def of_type
104
- @object.kind.wraps? ? @object.of_type : nil
140
+ self.class.resolve_of_type(object, context)
105
141
  end
106
142
  end
107
143
  end
@@ -2,7 +2,7 @@
2
2
  module GraphQL
3
3
  # Raised automatically when a field's resolve function returns `nil`
4
4
  # for a non-null field.
5
- class InvalidNullError < GraphQL::Error
5
+ class InvalidNullError < GraphQL::RuntimeError
6
6
  # @return [GraphQL::BaseType] The owner of {#field}
7
7
  attr_reader :parent_type
8
8
 
@@ -10,17 +10,23 @@ module GraphQL
10
10
  attr_reader :field
11
11
 
12
12
  # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
13
- attr_reader :ast_node
13
+ def ast_node
14
+ @ast_nodes.first
15
+ end
16
+
17
+ attr_reader :ast_nodes
14
18
 
15
19
  # @return [Boolean] indicates an array result caused the error
16
20
  attr_reader :is_from_array
17
21
 
18
- def initialize(parent_type, field, ast_node, is_from_array: false)
22
+ attr_accessor :path
23
+
24
+ def initialize(parent_type, field, ast_node_or_nodes, is_from_array: false, path: nil)
19
25
  @parent_type = parent_type
20
26
  @field = field
21
- @ast_node = ast_node
27
+ @ast_nodes = Array(ast_node_or_nodes)
22
28
  @is_from_array = is_from_array
23
-
29
+ @path = path
24
30
  # For List elements, identify the non-null error is for an
25
31
  # element and the required element type so it's not ambiguous
26
32
  # whether it was caused by a null instead of the list or a
@@ -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)
@@ -29,6 +29,7 @@ module GraphQL
29
29
  end
30
30
 
31
31
  extend Forwardable
32
+ include Schema::Member::HasDataloader
32
33
 
33
34
  # @return [Array<GraphQL::ExecutionError>] errors returned during execution
34
35
  attr_reader :errors
@@ -118,8 +119,8 @@ module GraphQL
118
119
  # @param error [GraphQL::ExecutionError] an execution error
119
120
  # @return [void]
120
121
  def add_error(error)
121
- if !error.is_a?(ExecutionError)
122
- raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
122
+ if !error.is_a?(GraphQL::RuntimeError)
123
+ raise TypeError, "expected error to be a GraphQL::RuntimeError, but was #{error.class}"
123
124
  end
124
125
  errors << error
125
126
  nil
@@ -4,7 +4,13 @@ module GraphQL
4
4
  class Query
5
5
  # This object can be `ctx` in places where there is no query
6
6
  class NullContext < Context
7
- include Singleton
7
+ def self.instance
8
+ @instance ||= self.new
9
+ end
10
+
11
+ def self.instance=(new_inst)
12
+ @instance = new_inst
13
+ end
8
14
 
9
15
  class NullQuery
10
16
  def after_lazy(value)
@@ -20,10 +26,10 @@ module GraphQL
20
26
  attr_reader :schema, :query, :warden, :dataloader
21
27
  def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?, :to_h
22
28
 
23
- def initialize
29
+ def initialize(schema: NullSchema)
24
30
  @query = NullQuery.new
25
31
  @dataloader = GraphQL::Dataloader::NullDataloader.new
26
- @schema = NullSchema
32
+ @schema = schema
27
33
  @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
28
34
  @types = @warden.visibility_profile
29
35
  freeze
@@ -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
@@ -164,6 +165,10 @@ module GraphQL
164
165
  true
165
166
  end
166
167
 
168
+ def authorizes?(_context)
169
+ self.method(:authorized?).owner != GraphQL::Schema::Argument
170
+ end
171
+
167
172
  def authorized?(obj, value, ctx)
168
173
  authorized_as_type?(obj, value, ctx, as_type: type)
169
174
  end
@@ -512,6 +512,7 @@ module GraphQL
512
512
  camelize: false,
513
513
  directives: prepare_directives(field_definition, type_resolver),
514
514
  resolver_method: resolve_method_name,
515
+ resolve_batch: resolve_method_name,
515
516
  )
516
517
 
517
518
  builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver)
@@ -528,6 +529,12 @@ module GraphQL
528
529
  field_instance = context.types.field(owner, field_name)
529
530
  context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
530
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
+ }
531
538
  end
532
539
 
533
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