graphql 1.10.0.pre1 → 1.10.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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)