graphql 1.10.0.pre1 → 1.10.0.pre2

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +1 -0
  3. data/lib/generators/graphql/install_generator.rb +1 -0
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/templates/base_field.erb +0 -4
  6. data/lib/generators/graphql/templates/base_mutation.erb +8 -0
  7. data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
  8. data/lib/generators/graphql/templates/mutation.erb +1 -1
  9. data/lib/generators/graphql/templates/schema.erb +1 -1
  10. data/lib/graphql.rb +4 -1
  11. data/lib/graphql/analysis/ast.rb +14 -13
  12. data/lib/graphql/analysis/ast/analyzer.rb +23 -4
  13. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  14. data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
  15. data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
  16. data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
  17. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  18. data/lib/graphql/base_type.rb +1 -1
  19. data/lib/graphql/directive.rb +0 -1
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  21. data/lib/graphql/execution/errors.rb +4 -8
  22. data/lib/graphql/execution/interpreter.rb +5 -11
  23. data/lib/graphql/execution/interpreter/runtime.rb +56 -48
  24. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  25. data/lib/graphql/execution/lookahead.rb +5 -5
  26. data/lib/graphql/execution/multiplex.rb +10 -0
  27. data/lib/graphql/function.rb +1 -1
  28. data/lib/graphql/input_object_type.rb +3 -2
  29. data/lib/graphql/interface_type.rb +1 -1
  30. data/lib/graphql/introspection/base_object.rb +2 -5
  31. data/lib/graphql/introspection/directive_type.rb +1 -1
  32. data/lib/graphql/introspection/entry_points.rb +6 -6
  33. data/lib/graphql/introspection/schema_type.rb +1 -6
  34. data/lib/graphql/introspection/type_type.rb +5 -5
  35. data/lib/graphql/language.rb +1 -1
  36. data/lib/graphql/language/block_string.rb +2 -2
  37. data/lib/graphql/language/definition_slice.rb +21 -10
  38. data/lib/graphql/language/document_from_schema_definition.rb +42 -42
  39. data/lib/graphql/language/lexer.rb +49 -48
  40. data/lib/graphql/language/lexer.rl +49 -48
  41. data/lib/graphql/language/nodes.rb +11 -8
  42. data/lib/graphql/language/parser.rb +4 -1
  43. data/lib/graphql/language/parser.y +4 -1
  44. data/lib/graphql/language/token.rb +1 -1
  45. data/lib/graphql/pagination/array_connection.rb +0 -1
  46. data/lib/graphql/pagination/connection.rb +31 -10
  47. data/lib/graphql/pagination/connections.rb +7 -2
  48. data/lib/graphql/pagination/relation_connection.rb +1 -7
  49. data/lib/graphql/query.rb +9 -4
  50. data/lib/graphql/query/arguments.rb +8 -1
  51. data/lib/graphql/query/literal_input.rb +2 -1
  52. data/lib/graphql/query/variables.rb +5 -1
  53. data/lib/graphql/relay/base_connection.rb +3 -3
  54. data/lib/graphql/relay/relation_connection.rb +9 -5
  55. data/lib/graphql/schema.rb +699 -153
  56. data/lib/graphql/schema/argument.rb +20 -4
  57. data/lib/graphql/schema/build_from_definition.rb +64 -31
  58. data/lib/graphql/schema/built_in_types.rb +5 -5
  59. data/lib/graphql/schema/directive.rb +16 -1
  60. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  61. data/lib/graphql/schema/directive/feature.rb +1 -1
  62. data/lib/graphql/schema/enum.rb +39 -3
  63. data/lib/graphql/schema/field.rb +39 -9
  64. data/lib/graphql/schema/field/connection_extension.rb +4 -4
  65. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  66. data/lib/graphql/schema/finder.rb +13 -11
  67. data/lib/graphql/schema/input_object.rb +109 -1
  68. data/lib/graphql/schema/interface.rb +8 -7
  69. data/lib/graphql/schema/introspection_system.rb +104 -36
  70. data/lib/graphql/schema/late_bound_type.rb +1 -0
  71. data/lib/graphql/schema/list.rb +26 -0
  72. data/lib/graphql/schema/loader.rb +10 -4
  73. data/lib/graphql/schema/member.rb +3 -0
  74. data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
  75. data/lib/graphql/schema/member/build_type.rb +1 -1
  76. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  77. data/lib/graphql/schema/member/has_fields.rb +15 -6
  78. data/lib/graphql/schema/member/instrumentation.rb +6 -1
  79. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  80. data/lib/graphql/schema/member/validates_input.rb +33 -0
  81. data/lib/graphql/schema/non_null.rb +25 -0
  82. data/lib/graphql/schema/object.rb +14 -1
  83. data/lib/graphql/schema/printer.rb +4 -3
  84. data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
  85. data/lib/graphql/schema/resolver.rb +20 -2
  86. data/lib/graphql/schema/scalar.rb +18 -3
  87. data/lib/graphql/schema/subscription.rb +1 -1
  88. data/lib/graphql/schema/timeout_middleware.rb +3 -2
  89. data/lib/graphql/schema/traversal.rb +1 -1
  90. data/lib/graphql/schema/type_expression.rb +22 -24
  91. data/lib/graphql/schema/validation.rb +17 -1
  92. data/lib/graphql/schema/warden.rb +46 -17
  93. data/lib/graphql/schema/wrapper.rb +1 -1
  94. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  95. data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
  96. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  97. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  98. data/lib/graphql/static_validation/type_stack.rb +2 -2
  99. data/lib/graphql/static_validation/validator.rb +1 -1
  100. data/lib/graphql/subscriptions.rb +38 -13
  101. data/lib/graphql/subscriptions/event.rb +24 -7
  102. data/lib/graphql/subscriptions/instrumentation.rb +1 -1
  103. data/lib/graphql/subscriptions/subscription_root.rb +0 -1
  104. data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
  105. data/lib/graphql/tracing/platform_tracing.rb +1 -2
  106. data/lib/graphql/tracing/skylight_tracing.rb +1 -0
  107. data/lib/graphql/unresolved_type_error.rb +2 -2
  108. data/lib/graphql/upgrader/member.rb +1 -1
  109. data/lib/graphql/version.rb +1 -1
  110. metadata +5 -2
@@ -116,7 +116,9 @@ module GraphQL
116
116
  elsif as_type.kind.input_object?
117
117
  as_type.arguments.each do |_name, input_obj_arg|
118
118
  input_obj_arg = input_obj_arg.type_class
119
- if value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
119
+ # TODO: this skips input objects whose values were alread replaced with application objects.
120
+ # See: https://github.com/rmosolgo/graphql-ruby/issues/2633
121
+ if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
120
122
  return false
121
123
  end
122
124
  end
@@ -141,6 +143,8 @@ module GraphQL
141
143
  argument
142
144
  end
143
145
 
146
+ attr_writer :type
147
+
144
148
  def type
145
149
  @type ||= Member::BuildType.parse_type(@type_expr, null: @null)
146
150
  rescue StandardError => err
@@ -150,13 +154,25 @@ module GraphQL
150
154
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
151
155
  # Used by the runtime.
152
156
  # @api private
153
- def prepare_value(obj, value)
157
+ def prepare_value(obj, value, context: nil)
158
+ if value.is_a?(GraphQL::Schema::InputObject)
159
+ value = value.prepare
160
+ end
161
+
154
162
  if @prepare.nil?
155
163
  value
156
164
  elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
157
- obj.public_send(@prepare, value)
165
+ if obj.nil?
166
+ # The problem here is, we _used to_ prepare while building variables.
167
+ # But now we don't have the runtime object there.
168
+ #
169
+ # This will have to be called later, when the runtime object _is_ available.
170
+ value
171
+ else
172
+ obj.public_send(@prepare, value)
173
+ end
158
174
  elsif @prepare.respond_to?(:call)
159
- @prepare.call(value, obj.context)
175
+ @prepare.call(value, context || obj.context)
160
176
  else
161
177
  raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
162
178
  end
@@ -5,9 +5,9 @@ module GraphQL
5
5
  class Schema
6
6
  module BuildFromDefinition
7
7
  class << self
8
- def from_definition(definition_string, default_resolve:, parser: DefaultParser)
8
+ def from_definition(definition_string, default_resolve:, using: {}, interpreter: true, parser: DefaultParser)
9
9
  document = parser.parse(definition_string)
10
- Builder.build(document, default_resolve: default_resolve)
10
+ Builder.build(document, default_resolve: default_resolve, using: using, interpreter: interpreter)
11
11
  end
12
12
  end
13
13
 
@@ -29,34 +29,39 @@ module GraphQL
29
29
  module Builder
30
30
  extend self
31
31
 
32
- def build(document, default_resolve: DefaultResolve)
32
+ def build(document, default_resolve: DefaultResolve, using: {}, interpreter: true)
33
33
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
34
34
 
35
35
  if default_resolve.is_a?(Hash)
36
36
  default_resolve = ResolveMap.new(default_resolve)
37
37
  end
38
38
 
39
- schema_definition = nil
39
+ schema_defns = document.definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) }
40
+ if schema_defns.size > 1
41
+ raise InvalidDocumentError.new('Must provide only one schema definition.')
42
+ end
43
+ schema_definition = schema_defns.first
40
44
  types = {}
41
45
  types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
42
46
  directives = {}
43
- type_resolver = ->(type) { -> { resolve_type(types, type) } }
47
+ type_resolver = ->(type) { resolve_type(types, type) }
44
48
 
45
49
  document.definitions.each do |definition|
46
50
  case definition
47
51
  when GraphQL::Language::Nodes::SchemaDefinition
48
- raise InvalidDocumentError.new('Must provide only one schema definition.') if schema_definition
49
- schema_definition = definition
52
+ nil # already handled
50
53
  when GraphQL::Language::Nodes::EnumTypeDefinition
51
- types[definition.name] = build_enum_type(definition, type_resolver).graphql_definition
54
+ types[definition.name] = build_enum_type(definition, type_resolver)
52
55
  when GraphQL::Language::Nodes::ObjectTypeDefinition
53
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve).graphql_definition
56
+ is_subscription_root = (definition.name == "Subscription" && (schema_definition.nil? || schema_definition.subscription.nil?)) || (schema_definition && (definition.name == schema_definition.subscription))
57
+ should_extend_subscription_root = is_subscription_root && interpreter
58
+ types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
54
59
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
55
- types[definition.name] = build_interface_type(definition, type_resolver).graphql_definition
60
+ types[definition.name] = build_interface_type(definition, type_resolver)
56
61
  when GraphQL::Language::Nodes::UnionTypeDefinition
57
- types[definition.name] = build_union_type(definition, type_resolver).graphql_definition
62
+ types[definition.name] = build_union_type(definition, type_resolver)
58
63
  when GraphQL::Language::Nodes::ScalarTypeDefinition
59
- types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve).graphql_definition
64
+ types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
60
65
  when GraphQL::Language::Nodes::InputObjectTypeDefinition
61
66
  types[definition.name] = build_input_object_type(definition, type_resolver)
62
67
  when GraphQL::Language::Nodes::DirectiveDefinition
@@ -90,10 +95,18 @@ module GraphQL
90
95
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
91
96
 
92
97
  Class.new(GraphQL::Schema) do
93
- query query_root_type
94
- mutation mutation_root_type
95
- subscription subscription_root_type
96
- orphan_types types.values
98
+ begin
99
+ # Add these first so that there's some chance of resolving late-bound types
100
+ orphan_types types.values
101
+ query query_root_type
102
+ mutation mutation_root_type
103
+ subscription subscription_root_type
104
+ rescue Schema::UnresolvedLateBoundTypeError => err
105
+ type_name = err.type.name
106
+ err_backtrace = err.backtrace
107
+ raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
108
+ end
109
+
97
110
  if default_resolve.respond_to?(:resolve_type)
98
111
  define_singleton_method(:resolve_type) do |*args|
99
112
  default_resolve.resolve_type(*args)
@@ -110,13 +123,19 @@ module GraphQL
110
123
  ast_node(schema_definition)
111
124
  end
112
125
 
113
- # Load caches, check for errors
114
- graphql_definition
126
+ if interpreter
127
+ use GraphQL::Execution::Interpreter
128
+ use GraphQL::Analysis::AST
129
+ end
130
+
131
+ using.each do |plugin, options|
132
+ use(plugin, options)
133
+ end
115
134
  end
116
135
  end
117
136
 
118
137
  NullResolveType = ->(type, obj, ctx) {
119
- raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
138
+ raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
120
139
  }
121
140
 
122
141
  def build_enum_type(enum_type_definition, type_resolver)
@@ -141,7 +160,7 @@ module GraphQL
141
160
  return unless deprecated_directive
142
161
 
143
162
  reason = deprecated_directive.arguments.find{ |a| a.name == 'reason' }
144
- return GraphQL::Directive::DEFAULT_DEPRECATION_REASON unless reason
163
+ return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON unless reason
145
164
 
146
165
  reason.value
147
166
  end
@@ -173,15 +192,19 @@ module GraphQL
173
192
  end
174
193
  end
175
194
 
176
- def build_object_type(object_type_definition, type_resolver, default_resolve:)
195
+ def build_object_type(object_type_definition, type_resolver, default_resolve:, extend_subscription_root:)
177
196
  builder = self
178
197
  type_def = nil
179
- typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def.graphql_definition, field, obj, args, ctx) }
198
+ typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
180
199
  Class.new(GraphQL::Schema::Object) do
181
200
  type_def = self
182
201
  graphql_name(object_type_definition.name)
183
202
  description(object_type_definition.description)
184
203
  ast_node(object_type_definition)
204
+ if extend_subscription_root
205
+ # This has to come before `field ...` configurations since it modifies them
206
+ extend Subscriptions::SubscriptionRoot
207
+ end
185
208
 
186
209
  object_type_definition.interfaces.each do |interface_name|
187
210
  interface_defn = type_resolver.call(interface_name)
@@ -268,32 +291,42 @@ module GraphQL
268
291
  field_definitions.map do |field_definition|
269
292
  type_name = resolve_type_name(field_definition.type)
270
293
 
271
- field = owner.field(
294
+ owner.field(
272
295
  field_definition.name,
273
296
  description: field_definition.description,
274
297
  type: type_resolver.call(field_definition.type),
275
298
  null: true,
276
299
  connection: type_name.end_with?("Connection"),
277
- resolve: ->(obj, args, ctx) { default_resolve.call(field.graphql_definition, obj, args, ctx) },
278
300
  deprecation_reason: build_deprecation_reason(field_definition.directives),
279
301
  ast_node: field_definition,
280
302
  method_conflict_warning: false,
281
303
  camelize: false,
282
304
  ) do
283
305
  builder.build_arguments(self, field_definition.arguments, type_resolver)
306
+
307
+ # Don't do this for interfaces
308
+ if default_resolve
309
+ # TODO fragile hack. formalize this API?
310
+ define_singleton_method :resolve_field_method do |obj, args, ctx|
311
+ default_resolve.call(self, obj.object, args, ctx)
312
+ end
313
+ end
284
314
  end
285
315
  end
286
316
  end
287
317
 
288
318
  def resolve_type(types, ast_node)
289
- type = GraphQL::Schema::TypeExpression.build_type(types, ast_node)
290
- if type.nil?
291
- while ast_node.respond_to?(:of_type)
292
- ast_node = ast_node.of_type
293
- end
294
- raise InvalidDocumentError.new("Type \"#{ast_node.name}\" not found in document.")
319
+ case ast_node
320
+ when GraphQL::Language::Nodes::TypeName
321
+ type_name = ast_node.name
322
+ types[type_name] ||= GraphQL::Schema::LateBoundType.new(type_name)
323
+ when GraphQL::Language::Nodes::NonNullType
324
+ resolve_type(types, ast_node.of_type).to_non_null_type
325
+ when GraphQL::Language::Nodes::ListType
326
+ resolve_type(types, ast_node.of_type).to_list_type
327
+ else
328
+ raise "Unexpected ast_node: #{ast_node.inspect}"
295
329
  end
296
- type
297
330
  end
298
331
 
299
332
  def resolve_type_name(type)
@@ -2,11 +2,11 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  BUILT_IN_TYPES = {
5
- "Int" => INT_TYPE,
6
- "String" => STRING_TYPE,
7
- "Float" => FLOAT_TYPE,
8
- "Boolean" => BOOLEAN_TYPE,
9
- "ID" => ID_TYPE,
5
+ "Int" => GraphQL::Types::Int,
6
+ "String" => GraphQL::Types::String,
7
+ "Float" => GraphQL::Types::Float,
8
+ "Boolean" => GraphQL::Types::Boolean,
9
+ "ID" => GraphQL::Types::ID,
10
10
  }
11
11
  end
12
12
  end
@@ -27,7 +27,7 @@ module GraphQL
27
27
  elsif @default_directive.nil?
28
28
  @default_directive = (superclass.respond_to?(:default_directive) ? superclass.default_directive : false)
29
29
  else
30
- @default_directive
30
+ !!@default_directive
31
31
  end
32
32
  end
33
33
 
@@ -47,6 +47,9 @@ module GraphQL
47
47
  arg_graphql = arg_defn.to_graphql
48
48
  defn.arguments[arg_graphql.name] = arg_graphql
49
49
  end
50
+ # Make a reference to a classic-style Arguments class
51
+ defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(defn)
52
+
50
53
  defn
51
54
  end
52
55
 
@@ -59,6 +62,18 @@ module GraphQL
59
62
  def resolve(object, arguments, context)
60
63
  yield
61
64
  end
65
+
66
+ def on_field?
67
+ locations.include?(FIELD)
68
+ end
69
+
70
+ def on_fragment?
71
+ locations.include?(FRAGMENT_SPREAD) && locations.include?(INLINE_FRAGMENT)
72
+ end
73
+
74
+ def on_operation?
75
+ locations.include?(QUERY) && locations.include?(MUTATION) && locations.include?(SUBSCRIPTION)
76
+ end
62
77
  end
63
78
 
64
79
  LOCATIONS = [
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ class Deprecated < GraphQL::Schema::Directive
6
+ description "Marks an element of a GraphQL schema as no longer supported."
7
+ locations(GraphQL::Schema::Directive::FIELD_DEFINITION, GraphQL::Schema::Directive::ENUM_VALUE)
8
+
9
+ reason_description = "Explains why this element was deprecated, usually also including a "\
10
+ "suggestion for how to access supported similar data. Formatted "\
11
+ "in [Markdown](https://daringfireball.net/projects/markdown/)."
12
+
13
+ argument :reason, String, reason_description, default_value: Directive::DEFAULT_DEPRECATION_REASON, required: false
14
+ default_directive true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -58,7 +58,7 @@ module GraphQL
58
58
  # @param context [GraphQL::Query::Context]
59
59
  # @return [Boolean] If truthy, execution will continue
60
60
  def self.enabled?(flag_name, object, context)
61
- raise NotImplementedError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})"
61
+ raise GraphQL::RequiredImplementationMissingError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})"
62
62
  end
63
63
  end
64
64
  end
@@ -21,11 +21,9 @@ module GraphQL
21
21
  class Schema
22
22
  class Enum < GraphQL::Schema::Member
23
23
  extend GraphQL::Schema::Member::AcceptsDefinition
24
+ extend GraphQL::Schema::Member::ValidatesInput
24
25
 
25
26
  class << self
26
- extend Forwardable
27
- def_delegators :graphql_definition, :coerce_isolated_input, :coerce_isolated_result, :coerce_input, :coerce_result
28
-
29
27
  # Define a value for this enum
30
28
  # @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
31
29
  # @param description [String], the GraphQL description for this value, present in documentation
@@ -73,6 +71,44 @@ module GraphQL
73
71
  GraphQL::TypeKinds::ENUM
74
72
  end
75
73
 
74
+ def validate_non_null_input(value_name, ctx)
75
+ result = GraphQL::Query::InputValidationResult.new
76
+
77
+ allowed_values = ctx.warden.enum_values(self)
78
+ matching_value = allowed_values.find { |v| v.graphql_name == value_name }
79
+
80
+ if matching_value.nil?
81
+ result.add_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}")
82
+ end
83
+
84
+ result
85
+ end
86
+
87
+ def coerce_result(value, ctx)
88
+ warden = ctx.warden
89
+ all_values = warden ? warden.enum_values(self) : values.each_value
90
+ enum_value = all_values.find { |val| val.value == value }
91
+ if enum_value
92
+ enum_value.graphql_name
93
+ else
94
+ raise(GraphQL::EnumType::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
95
+ end
96
+ end
97
+
98
+ def coerce_input(value_name, ctx)
99
+ all_values = ctx.warden ? ctx.warden.enum_values(self) : values.each_value
100
+
101
+ if v = all_values.find { |val| val.graphql_name == value_name }
102
+ v.value
103
+ elsif v = all_values.find { |val| val.value == value_name }
104
+ # this is for matching default values, which are "inputs", but they're
105
+ # the Ruby value, not the GraphQL string.
106
+ v.value
107
+ else
108
+ nil
109
+ end
110
+ end
111
+
76
112
  private
77
113
 
78
114
  def own_values
@@ -15,6 +15,7 @@ module GraphQL
15
15
  include GraphQL::Schema::Member::HasArguments
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasPath
18
+ extend GraphQL::Schema::FindInheritedValue
18
19
 
19
20
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
20
21
  attr_reader :name
@@ -35,7 +36,7 @@ module GraphQL
35
36
  attr_reader :resolver_method
36
37
 
37
38
  # @return [Class] The type that this field belongs to
38
- attr_reader :owner
39
+ attr_accessor :owner
39
40
 
40
41
  # @return [Symbol] the original name of the field, passed in by the user
41
42
  attr_reader :original_name
@@ -51,7 +52,7 @@ module GraphQL
51
52
  attr_reader :trace
52
53
 
53
54
  # @return [String, nil]
54
- attr_reader :subscription_scope
55
+ attr_accessor :subscription_scope
55
56
 
56
57
  # Create a field instance from a list of arguments, keyword arguments, and a block.
57
58
  #
@@ -136,6 +137,25 @@ module GraphQL
136
137
  end
137
138
  end
138
139
 
140
+ # This extension is applied to fields when {#connection?} is true.
141
+ #
142
+ # You can override it in your base field definition.
143
+ # @return [Class] A {FieldExtension} subclass for implementing pagination behavior.
144
+ # @example Configuring a custom extension
145
+ # class Types::BaseField < GraphQL::Schema::Field
146
+ # connection_extension(MyCustomExtension)
147
+ # end
148
+ def self.connection_extension(new_extension_class = nil)
149
+ if new_extension_class
150
+ @connection_extension = new_extension_class
151
+ else
152
+ @connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension)
153
+ end
154
+ end
155
+
156
+ # @return Boolean
157
+ attr_reader :relay_node_field
158
+
139
159
  # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
140
160
  def method_conflict_warning?
141
161
  @method_conflict_warning
@@ -256,7 +276,7 @@ module GraphQL
256
276
  # The problem with putting this after the definition_block
257
277
  # is that it would override arguments
258
278
  if connection?
259
- self.extension(ConnectionExtension)
279
+ self.extension(self.class.connection_extension)
260
280
  end
261
281
 
262
282
  if definition_block
@@ -340,7 +360,7 @@ module GraphQL
340
360
  end
341
361
  end
342
362
 
343
- def complexity(new_complexity)
363
+ def complexity(new_complexity = nil)
344
364
  case new_complexity
345
365
  when Proc
346
366
  if new_complexity.parameters.size != 3
@@ -353,6 +373,8 @@ module GraphQL
353
373
  end
354
374
  when Numeric
355
375
  @complexity = new_complexity
376
+ when nil
377
+ @complexity
356
378
  else
357
379
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
358
380
  end
@@ -427,12 +449,20 @@ module GraphQL
427
449
 
428
450
  # Ok, `self` isn't a class, but this is for consistency with the classes
429
451
  field_defn.metadata[:type_class] = self
430
-
452
+ field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
431
453
  field_defn
432
454
  end
433
455
 
456
+ attr_writer :type
457
+
434
458
  def type
435
- @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
459
+ @type ||= if @function
460
+ Member::BuildType.parse_type(@function.type, null: false)
461
+ elsif @field
462
+ Member::BuildType.parse_type(@field.type, null: false)
463
+ else
464
+ Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
465
+ end
436
466
  rescue GraphQL::Schema::InvalidDocumentError => err
437
467
  # Let this propagate up
438
468
  raise err
@@ -523,7 +553,7 @@ module GraphQL
523
553
  else
524
554
  extended_obj
525
555
  end
526
- @resolver_class.new(object: resolver_obj, context: ctx)
556
+ @resolver_class.new(object: resolver_obj, context: ctx, field: self)
527
557
  else
528
558
  extended_obj
529
559
  end
@@ -597,7 +627,7 @@ module GraphQL
597
627
  elsif ctx.respond_to?(extra_name)
598
628
  ctx.public_send(extra_name)
599
629
  else
600
- raise NotImplementedError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
630
+ raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
601
631
  end
602
632
  end
603
633
 
@@ -640,7 +670,7 @@ module GraphQL
640
670
  if extended_obj.is_a?(GraphQL::Schema::Object)
641
671
  extended_obj = extended_obj.object
642
672
  end
643
- extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx)
673
+ extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx, field: self)
644
674
  end
645
675
 
646
676
  if extended_obj.respond_to?(@resolver_method)