graphql 1.12.21 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/mutation_generator.rb +1 -1
  3. data/lib/generators/graphql/type_generator.rb +0 -1
  4. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  5. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  6. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  7. data/lib/graphql/backtrace/table.rb +1 -1
  8. data/lib/graphql/dataloader.rb +55 -22
  9. data/lib/graphql/directive.rb +0 -4
  10. data/lib/graphql/enum_type.rb +5 -1
  11. data/lib/graphql/execution/errors.rb +1 -0
  12. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  13. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  14. data/lib/graphql/execution/interpreter/runtime.rb +20 -12
  15. data/lib/graphql/execution/lookahead.rb +2 -2
  16. data/lib/graphql/execution/multiplex.rb +1 -1
  17. data/lib/graphql/introspection/directive_type.rb +1 -1
  18. data/lib/graphql/introspection/entry_points.rb +2 -2
  19. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  20. data/lib/graphql/introspection/field_type.rb +2 -2
  21. data/lib/graphql/introspection/input_value_type.rb +4 -4
  22. data/lib/graphql/introspection/schema_type.rb +2 -2
  23. data/lib/graphql/introspection/type_type.rb +10 -10
  24. data/lib/graphql/language/block_string.rb +0 -4
  25. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  26. data/lib/graphql/language/lexer.rb +0 -3
  27. data/lib/graphql/language/lexer.rl +0 -4
  28. data/lib/graphql/language/nodes.rb +2 -1
  29. data/lib/graphql/language/parser.rb +442 -434
  30. data/lib/graphql/language/parser.y +5 -4
  31. data/lib/graphql/language/printer.rb +6 -1
  32. data/lib/graphql/language/sanitized_printer.rb +5 -5
  33. data/lib/graphql/language/token.rb +0 -4
  34. data/lib/graphql/name_validator.rb +0 -4
  35. data/lib/graphql/query/arguments.rb +1 -1
  36. data/lib/graphql/query/arguments_cache.rb +1 -1
  37. data/lib/graphql/query/context.rb +5 -2
  38. data/lib/graphql/query/literal_input.rb +1 -1
  39. data/lib/graphql/query/null_context.rb +12 -7
  40. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  41. data/lib/graphql/query/variables.rb +5 -1
  42. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  43. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  44. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  45. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  46. data/lib/graphql/rubocop.rb +4 -0
  47. data/lib/graphql/schema/addition.rb +37 -28
  48. data/lib/graphql/schema/argument.rb +6 -6
  49. data/lib/graphql/schema/build_from_definition.rb +5 -5
  50. data/lib/graphql/schema/directive/feature.rb +1 -1
  51. data/lib/graphql/schema/directive/flagged.rb +2 -2
  52. data/lib/graphql/schema/directive/include.rb +1 -1
  53. data/lib/graphql/schema/directive/skip.rb +1 -1
  54. data/lib/graphql/schema/directive/transform.rb +1 -1
  55. data/lib/graphql/schema/directive.rb +2 -2
  56. data/lib/graphql/schema/enum.rb +57 -9
  57. data/lib/graphql/schema/enum_value.rb +4 -0
  58. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  59. data/lib/graphql/schema/field.rb +92 -17
  60. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  61. data/lib/graphql/schema/finder.rb +5 -5
  62. data/lib/graphql/schema/input_object.rb +6 -5
  63. data/lib/graphql/schema/interface.rb +8 -19
  64. data/lib/graphql/schema/member/accepts_definition.rb +8 -1
  65. data/lib/graphql/schema/member/build_type.rb +0 -4
  66. data/lib/graphql/schema/member/has_arguments.rb +55 -13
  67. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  68. data/lib/graphql/schema/member/has_fields.rb +76 -18
  69. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  70. data/lib/graphql/schema/member.rb +1 -0
  71. data/lib/graphql/schema/object.rb +7 -74
  72. data/lib/graphql/schema/printer.rb +1 -1
  73. data/lib/graphql/schema/relay_classic_mutation.rb +29 -3
  74. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  75. data/lib/graphql/schema/resolver.rb +19 -5
  76. data/lib/graphql/schema/subscription.rb +11 -1
  77. data/lib/graphql/schema/type_expression.rb +1 -1
  78. data/lib/graphql/schema/type_membership.rb +18 -4
  79. data/lib/graphql/schema/union.rb +6 -1
  80. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  81. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  82. data/lib/graphql/schema/warden.rb +116 -52
  83. data/lib/graphql/schema.rb +87 -15
  84. data/lib/graphql/static_validation/base_visitor.rb +5 -5
  85. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  86. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  87. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  88. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  89. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  90. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  91. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  92. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +6 -4
  93. data/lib/graphql/subscriptions/event.rb +20 -12
  94. data/lib/graphql/subscriptions.rb +17 -19
  95. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  96. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  97. data/lib/graphql/version.rb +1 -1
  98. data/lib/graphql.rb +9 -31
  99. metadata +10 -5
@@ -61,9 +61,9 @@ module GraphQL
61
61
  defn.default_directive = self.default_directive
62
62
  defn.ast_node = ast_node
63
63
  defn.metadata[:type_class] = self
64
- arguments.each do |name, arg_defn|
64
+ all_argument_definitions.each do |arg_defn|
65
65
  arg_graphql = arg_defn.to_graphql
66
- defn.arguments[arg_graphql.name] = arg_graphql
66
+ defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
67
67
  end
68
68
  # Make a reference to a classic-style Arguments class
69
69
  defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(defn)
@@ -46,18 +46,66 @@ module GraphQL
46
46
  def value(*args, **kwargs, &block)
47
47
  kwargs[:owner] = self
48
48
  value = enum_value_class.new(*args, **kwargs, &block)
49
- if own_values.key?(value.graphql_name)
50
- raise ArgumentError, "#{value.graphql_name} is already defined for #{self.graphql_name}, please remove one of the definitions."
49
+ key = value.graphql_name
50
+ prev_value = own_values[key]
51
+ case prev_value
52
+ when nil
53
+ own_values[key] = value
54
+ when GraphQL::Schema::EnumValue
55
+ own_values[key] = [prev_value, value]
56
+ when Array
57
+ prev_value << value
58
+ else
59
+ raise "Invariant: Unexpected enum value for #{key.inspect}: #{prev_value.inspect}"
60
+ end
61
+ value
62
+ end
63
+
64
+ # @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
65
+ def enum_values(context = GraphQL::Query::NullContext)
66
+ inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
67
+ visible_values = []
68
+ warden = Warden.from_context(context)
69
+ own_values.each do |key, values_entry|
70
+ if (v = Warden.visible_entry?(:visible_enum_value?, values_entry, context, warden))
71
+ visible_values << v
72
+ end
73
+ end
74
+
75
+ if inherited_values
76
+ # Local values take precedence over inherited ones
77
+ inherited_values.each do |i_val|
78
+ if !visible_values.any? { |v| v.graphql_name == i_val.graphql_name }
79
+ visible_values << i_val
80
+ end
81
+ end
82
+ end
83
+
84
+ visible_values
85
+ end
86
+
87
+ # @return [Array<Schema::EnumValue>] An unfiltered list of all definitions
88
+ def all_enum_value_definitions
89
+ all_defns = if superclass.respond_to?(:all_enum_value_definitions)
90
+ superclass.all_enum_value_definitions
91
+ else
92
+ []
51
93
  end
52
- own_values[value.graphql_name] = value
53
- nil
94
+
95
+ @own_values && @own_values.each do |_key, value|
96
+ if value.is_a?(Array)
97
+ all_defns.concat(value)
98
+ else
99
+ all_defns << value
100
+ end
101
+ end
102
+
103
+ all_defns
54
104
  end
55
105
 
56
- # @return [Hash<String => GraphQL::Schema::Enum::Value>] Possible values of this enum, keyed by name
57
- def values
58
- inherited_values = superclass <= GraphQL::Schema::Enum ? superclass.values : {}
59
- # Local values take precedence over inherited ones
60
- inherited_values.merge(own_values)
106
+ # @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
107
+ def values(context = GraphQL::Query::NullContext)
108
+ enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
61
109
  end
62
110
 
63
111
  # @return [GraphQL::EnumType]
@@ -85,6 +85,10 @@ module GraphQL
85
85
  enum_value
86
86
  end
87
87
 
88
+ def inspect
89
+ "#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}>"
90
+ end
91
+
88
92
  def visible?(_ctx); true; end
89
93
  def accessible?(_ctx); true; end
90
94
  def authorized?(_ctx); true; end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  value.after_value ||= original_arguments[:after]
43
43
  value.last_value ||= original_arguments[:last]
44
44
  value.before_value ||= original_arguments[:before]
45
- value.arguments ||= original_arguments
45
+ value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
46
46
  value.field ||= field
47
47
  if field.has_max_page_size? && !value.has_max_page_size_override?
48
48
  value.max_page_size = field.max_page_size
@@ -5,10 +5,6 @@ require "graphql/schema/field/scope_extension"
5
5
  module GraphQL
6
6
  class Schema
7
7
  class Field
8
- if !String.method_defined?(:-@)
9
- using GraphQL::StringDedupBackport
10
- end
11
-
12
8
  include GraphQL::Schema::Member::CachedGraphQLDefinition
13
9
  include GraphQL::Schema::Member::AcceptsDefinition
14
10
  include GraphQL::Schema::Member::HasArguments
@@ -61,7 +57,7 @@ module GraphQL
61
57
  end
62
58
 
63
59
  def inspect
64
- "#<#{self.class} #{path}#{arguments.any? ? "(...)" : ""}: #{type.to_type_signature}>"
60
+ "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
65
61
  end
66
62
 
67
63
  alias :mutation :resolver
@@ -212,7 +208,7 @@ module GraphQL
212
208
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
213
209
  # @param validates [Array<Hash>] Configurations for validating this field
214
210
  # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
215
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
211
+ def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
216
212
  if name.nil?
217
213
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
218
214
  end
@@ -220,9 +216,6 @@ module GraphQL
220
216
  if type.nil?
221
217
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
222
218
  end
223
- if null.nil?
224
- raise ArgumentError, "missing keyword argument null:"
225
- end
226
219
  end
227
220
  if (field || function || resolve) && extras.any?
228
221
  raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
@@ -281,10 +274,15 @@ module GraphQL
281
274
  @legacy_edge_class = legacy_edge_class
282
275
 
283
276
  arguments.each do |name, arg|
284
- if arg.is_a?(Hash)
277
+ case arg
278
+ when Hash
285
279
  argument(name: name, **arg)
286
- else
280
+ when GraphQL::Schema::Argument
287
281
  add_argument(arg)
282
+ when Array
283
+ arg.each { |a| add_argument(a) }
284
+ else
285
+ raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
288
286
  end
289
287
  end
290
288
 
@@ -411,6 +409,57 @@ module GraphQL
411
409
  end
412
410
  end
413
411
 
412
+ def calculate_complexity(query:, nodes:, child_complexity:)
413
+ if respond_to?(:complexity_for)
414
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
415
+ complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
416
+ elsif connection?
417
+ arguments = query.arguments_for(nodes.first, self)
418
+ max_possible_page_size = nil
419
+ if arguments[:first]
420
+ max_possible_page_size = arguments[:first]
421
+ end
422
+ if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
423
+ max_possible_page_size = arguments[:last]
424
+ end
425
+
426
+ if max_possible_page_size.nil?
427
+ max_possible_page_size = max_page_size || query.schema.default_max_page_size
428
+ end
429
+
430
+ if max_possible_page_size.nil?
431
+ raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
432
+ else
433
+ metadata_complexity = 0
434
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
435
+
436
+ if (page_info_lookahead = lookahead.selection(:page_info)).selected?
437
+ metadata_complexity += 1 # pageInfo
438
+ metadata_complexity += page_info_lookahead.selections.size # subfields
439
+ end
440
+
441
+ if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
442
+ metadata_complexity += 1
443
+ end
444
+ # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
445
+ items_complexity = child_complexity - metadata_complexity
446
+ # Add 1 for _this_ field
447
+ 1 + (max_possible_page_size * items_complexity) + metadata_complexity
448
+ end
449
+ else
450
+ defined_complexity = complexity
451
+ case defined_complexity
452
+ when Proc
453
+ arguments = query.arguments_for(nodes.first, self)
454
+ defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
455
+ when Numeric
456
+ defined_complexity + child_complexity
457
+ else
458
+ raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
459
+ end
460
+ end
461
+ end
462
+
414
463
  def complexity(new_complexity = nil)
415
464
  case new_complexity
416
465
  when Proc
@@ -493,9 +542,9 @@ module GraphQL
493
542
  field_defn.subscription_scope = @subscription_scope
494
543
  field_defn.ast_node = ast_node
495
544
 
496
- arguments.each do |name, defn|
545
+ all_argument_definitions.each do |defn|
497
546
  arg_graphql = defn.to_graphql
498
- field_defn.arguments[arg_graphql.name] = arg_graphql
547
+ field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
499
548
  end
500
549
 
501
550
  # Support a passed-in proc, one way or another
@@ -559,10 +608,36 @@ module GraphQL
559
608
  # The resolver will check itself during `resolve()`
560
609
  @resolver_class.authorized?(object, context)
561
610
  else
611
+ if (arg_values = context[:current_arguments])
612
+ # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
613
+ using_arg_values = true
614
+ arg_values = arg_values.argument_values
615
+ else
616
+ arg_values = args
617
+ using_arg_values = false
618
+ end
562
619
  # Faster than `.any?`
563
- arguments.each_value do |arg|
564
- if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
565
- return false
620
+ arguments(context).each_value do |arg|
621
+ arg_key = arg.keyword
622
+ if arg_values.key?(arg_key)
623
+ arg_value = arg_values[arg_key]
624
+ if using_arg_values
625
+ if arg_value.default_used?
626
+ # pass -- no auth required for default used
627
+ next
628
+ else
629
+ application_arg_value = arg_value.value
630
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
631
+ application_arg_value.keyword_arguments
632
+ end
633
+ end
634
+ else
635
+ application_arg_value = arg_value
636
+ end
637
+
638
+ if !arg.authorized?(object, application_arg_value, context)
639
+ return false
640
+ end
566
641
  end
567
642
  end
568
643
  true
@@ -659,7 +734,7 @@ module GraphQL
659
734
  ruby_kwargs = graphql_args.to_kwargs
660
735
  maybe_lazies = []
661
736
  # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
662
- arguments.each do |name, arg_defn|
737
+ arguments(field_ctx).each do |name, arg_defn|
663
738
  ruby_kwargs_key = arg_defn.keyword
664
739
 
665
740
  if ruby_kwargs.key?(ruby_kwargs_key)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  module FindInheritedValue
@@ -38,7 +38,7 @@ module GraphQL
38
38
 
39
39
  find_in_directive(directive, path: path)
40
40
  else
41
- type = schema.get_type(type_or_directive)
41
+ type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time
42
42
 
43
43
  if type.nil?
44
44
  raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
@@ -56,7 +56,7 @@ module GraphQL
56
56
 
57
57
  def find_in_directive(directive, path:)
58
58
  argument_name = path.shift
59
- argument = directive.arguments[argument_name]
59
+ argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
60
60
 
61
61
  if argument.nil?
62
62
  raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
@@ -102,7 +102,7 @@ module GraphQL
102
102
 
103
103
  def find_in_field(field, path:)
104
104
  argument_name = path.shift
105
- argument = field.arguments[argument_name]
105
+ argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
106
106
 
107
107
  if argument.nil?
108
108
  raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
@@ -119,7 +119,7 @@ module GraphQL
119
119
 
120
120
  def find_in_input_object(input_object, path:)
121
121
  field_name = path.shift
122
- input_field = input_object.arguments[field_name]
122
+ input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
123
123
 
124
124
  if input_field.nil?
125
125
  raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
@@ -136,7 +136,7 @@ module GraphQL
136
136
 
137
137
  def find_in_enum_type(enum_type, path:)
138
138
  value_name = path.shift
139
- enum_value = enum_type.values[value_name]
139
+ enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime
140
140
 
141
141
  if enum_value.nil?
142
142
  raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
@@ -31,7 +31,7 @@ module GraphQL
31
31
  end
32
32
  # Apply prepares, not great to have it duplicated here.
33
33
  maybe_lazies = []
34
- self.class.arguments.each_value do |arg_defn|
34
+ self.class.arguments(context).each_value do |arg_defn|
35
35
  ruby_kwargs_key = arg_defn.keyword
36
36
 
37
37
  if @ruby_style_hash.key?(ruby_kwargs_key)
@@ -129,6 +129,7 @@ module GraphQL
129
129
  self[#{method_name.inspect}]
130
130
  end
131
131
  RUBY
132
+ argument_defn
132
133
  end
133
134
 
134
135
  def to_graphql
@@ -138,8 +139,8 @@ module GraphQL
138
139
  type_defn.metadata[:type_class] = self
139
140
  type_defn.mutation = mutation
140
141
  type_defn.ast_node = ast_node
141
- arguments.each do |name, arg|
142
- type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
142
+ all_argument_definitions.each do |arg|
143
+ type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition # rubocop:disable Development/ContextIsPassedCop -- legacy-related
143
144
  end
144
145
  # Make a reference to a classic-style Arguments class
145
146
  self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
@@ -172,7 +173,7 @@ module GraphQL
172
173
  end
173
174
 
174
175
  # Inject missing required arguments
175
- missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
176
+ missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
176
177
  if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
177
178
  m[argument_name] = nil
178
179
  end
@@ -223,7 +224,7 @@ module GraphQL
223
224
 
224
225
  result = {}
225
226
 
226
- arguments.each do |input_key, input_field_defn|
227
+ arguments(ctx).each do |input_key, input_field_defn|
227
228
  input_value = value[input_key]
228
229
  if value.key?(input_key)
229
230
  result[input_key] = if input_value.nil?
@@ -16,6 +16,7 @@ module GraphQL
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasUnresolvedTypeError
18
18
  include GraphQL::Schema::Member::HasDirectives
19
+ include GraphQL::Schema::Member::HasInterfaces
19
20
 
20
21
  # Methods defined in this block will be:
21
22
  # - Added as class methods to this interface
@@ -57,9 +58,10 @@ module GraphQL
57
58
  child_class.extend(Schema::Interface::DefinitionMethods)
58
59
 
59
60
  child_class.type_membership_class(self.type_membership_class)
60
- child_class.own_interfaces << self
61
- child_class.interfaces.reverse_each do |interface_defn|
62
- child_class.extend(interface_defn::DefinitionMethods)
61
+ child_class.ancestors.reverse_each do |ancestor|
62
+ if ancestor.const_defined?(:DefinitionMethods)
63
+ child_class.extend(ancestor::DefinitionMethods)
64
+ end
63
65
  end
64
66
 
65
67
  # Use an instance variable to tell whether it's been included previously or not;
@@ -73,16 +75,13 @@ module GraphQL
73
75
  end
74
76
  child_class.introspection(introspection)
75
77
  child_class.description(description)
76
- if overridden_graphql_name
77
- child_class.graphql_name(overridden_graphql_name)
78
- end
79
78
  # If interfaces are mixed into each other, only define this class once
80
79
  if !child_class.const_defined?(:UnresolvedTypeError, false)
81
80
  add_unresolved_type_error(child_class)
82
81
  end
83
82
  elsif child_class < GraphQL::Schema::Object
84
83
  # This is being included into an object type, make sure it's using `implements(...)`
85
- backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
84
+ backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
86
85
  if !backtrace_line
87
86
  raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
88
87
  end
@@ -108,9 +107,9 @@ module GraphQL
108
107
  type_defn.orphan_types = orphan_types
109
108
  type_defn.type_membership_class = self.type_membership_class
110
109
  type_defn.ast_node = ast_node
111
- fields.each do |field_name, field_inst|
110
+ fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
112
111
  field_defn = field_inst.graphql_definition
113
- type_defn.fields[field_defn.name] = field_defn
112
+ type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
114
113
  end
115
114
  type_defn.metadata[:type_class] = self
116
115
  if respond_to?(:resolve_type)
@@ -122,16 +121,6 @@ module GraphQL
122
121
  def kind
123
122
  GraphQL::TypeKinds::INTERFACE
124
123
  end
125
-
126
- protected
127
-
128
- def own_interfaces
129
- @own_interfaces ||= []
130
- end
131
-
132
- def interfaces
133
- own_interfaces + (own_interfaces.map { |i| i.own_interfaces }).flatten
134
- end
135
124
  end
136
125
 
137
126
  # Extend this _after_ `DefinitionMethods` is defined, so it will be used
@@ -85,8 +85,15 @@ module GraphQL
85
85
  define_method(name) do |*args|
86
86
  if args.any?
87
87
  instance_variable_set(ivar_name, args)
88
+ else
89
+ if (v = instance_variable_get(ivar_name))
90
+ v
91
+ elsif (ancestor = ancestors.find { |i| i.respond_to?(name) && i != self })
92
+ ancestor.public_send(name)
93
+ else
94
+ nil
95
+ end
88
96
  end
89
- instance_variable_get(ivar_name) || ((int = interfaces.first { |i| i.respond_to?()}) && int.public_send(name))
90
97
  end
91
98
  end
92
99
  end
@@ -4,10 +4,6 @@ module GraphQL
4
4
  class Member
5
5
  # @api private
6
6
  module BuildType
7
- if !String.method_defined?(:match?)
8
- using GraphQL::StringMatchBackport
9
- end
10
-
11
7
  LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
12
8
 
13
9
  module_function
@@ -78,30 +78,72 @@ module GraphQL
78
78
  # @return [GraphQL::Schema::Argument]
79
79
  def add_argument(arg_defn)
80
80
  @own_arguments ||= {}
81
- own_arguments[arg_defn.name] = arg_defn
81
+ prev_defn = own_arguments[arg_defn.name]
82
+ case prev_defn
83
+ when nil
84
+ own_arguments[arg_defn.name] = arg_defn
85
+ when Array
86
+ prev_defn << arg_defn
87
+ when GraphQL::Schema::Argument
88
+ own_arguments[arg_defn.name] = [prev_defn, arg_defn]
89
+ else
90
+ raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
91
+ end
82
92
  arg_defn
83
93
  end
84
94
 
85
95
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
86
- def arguments
87
- inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments : nil)
96
+ def arguments(context = GraphQL::Query::NullContext)
97
+ inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments(context) : nil)
88
98
  # Local definitions override inherited ones
99
+ if own_arguments.any?
100
+ own_arguments_that_apply = {}
101
+ own_arguments.each do |name, args_entry|
102
+ if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
103
+ own_arguments_that_apply[visible_defn.graphql_name] = visible_defn
104
+ end
105
+ end
106
+ end
107
+
89
108
  if inherited_arguments
90
- inherited_arguments.merge(own_arguments)
109
+ if own_arguments_that_apply
110
+ inherited_arguments.merge(own_arguments_that_apply)
111
+ else
112
+ inherited_arguments
113
+ end
91
114
  else
92
- own_arguments
115
+ # might be nil if there are actually no arguments
116
+ own_arguments_that_apply || own_arguments
93
117
  end
94
118
  end
95
119
 
96
- # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
97
- def get_argument(argument_name)
98
- a = own_arguments[argument_name]
120
+ def all_argument_definitions
121
+ if self.is_a?(Class)
122
+ all_defns = {}
123
+ ancestors.reverse_each do |ancestor|
124
+ if ancestor.respond_to?(:own_arguments)
125
+ all_defns.merge!(ancestor.own_arguments)
126
+ end
127
+ end
128
+ else
129
+ all_defns = own_arguments
130
+ end
131
+ all_defns = all_defns.values
132
+ all_defns.flatten!
133
+ all_defns
134
+ end
99
135
 
100
- if a || !self.is_a?(Class)
101
- a
136
+ # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
137
+ def get_argument(argument_name, context = GraphQL::Query::NullContext)
138
+ warden = Warden.from_context(context)
139
+ if !self.is_a?(Class)
140
+ a = own_arguments[argument_name]
141
+ a && Warden.visible_entry?(:visible_argument?, a, context, warden)
102
142
  else
103
143
  for ancestor in ancestors
104
- if ancestor.respond_to?(:own_arguments) && a = ancestor.own_arguments[argument_name]
144
+ if ancestor.respond_to?(:own_arguments) &&
145
+ (a = ancestor.own_arguments[argument_name]) &&
146
+ (a = Warden.visible_entry?(:visible_argument?, a, context, warden))
105
147
  return a
106
148
  end
107
149
  end
@@ -125,7 +167,7 @@ module GraphQL
125
167
  # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
126
168
  def coerce_arguments(parent_object, values, context, &block)
127
169
  # Cache this hash to avoid re-merging it
128
- arg_defns = self.arguments
170
+ arg_defns = self.arguments(context)
129
171
  total_args_count = arg_defns.size
130
172
 
131
173
  finished_args = nil
@@ -191,7 +233,7 @@ module GraphQL
191
233
  def arguments_statically_coercible?
192
234
  return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
193
235
 
194
- @arguments_statically_coercible = arguments.each_value.all?(&:statically_coercible?)
236
+ @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
195
237
  end
196
238
 
197
239
  module ArgumentClassAccessor
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
8
8
  def deprecation_reason
9
9
  dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
10
- dir && dir.arguments[:reason]
10
+ dir && dir.arguments[:reason] # rubocop:disable Development/ContextIsPassedCop -- definition-related
11
11
  end
12
12
 
13
13
  # Set the deprecation reason for this member, or remove it by assigning `nil`