graphql 1.12.25 → 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 +4 -8
  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 +3 -14
  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 +11 -9
  49. data/lib/graphql/schema/build_from_definition.rb +12 -13
  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 -6
  56. data/lib/graphql/schema/enum.rb +57 -9
  57. data/lib/graphql/schema/enum_value.rb +5 -1
  58. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  59. data/lib/graphql/schema/field.rb +92 -18
  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 +11 -13
  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 +8 -15
  89. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -3
  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 +14 -6
@@ -325,8 +325,9 @@ rule
325
325
  | EXTEND TYPE name implements { result = make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
326
326
 
327
327
  interface_type_extension:
328
- EXTEND INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY { result = make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: val[5], position_source: val[0]) }
329
- | EXTEND INTERFACE name directives_list { result = make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: [], position_source: val[0]) }
328
+ EXTEND INTERFACE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: val[6], position_source: val[0]) }
329
+ | EXTEND INTERFACE name implements_opt directives_list { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: [], position_source: val[0]) }
330
+ | EXTEND INTERFACE name implements { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
330
331
 
331
332
  union_type_extension:
332
333
  EXTEND UNION name directives_list_opt EQUALS union_members { result = make_node(:UnionTypeExtension, name: val[2], directives: val[3], types: val[5], position_source: val[0]) }
@@ -397,8 +398,8 @@ rule
397
398
  | field_definition_list field_definition { val[0] << val[1] }
398
399
 
399
400
  interface_type_definition:
400
- description_opt INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY {
401
- result = make_node(:InterfaceTypeDefinition, name: val[2], directives: val[3], fields: val[5], description: val[0] || get_description(val[1]), definition_line: val[1].line, position_source: val[0] || val[1])
401
+ description_opt INTERFACE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY {
402
+ result = make_node(:InterfaceTypeDefinition, name: val[2], interfaces: val[3], directives: val[4], fields: val[6], description: val[0] || get_description(val[1]), definition_line: val[1].line, position_source: val[0] || val[1])
402
403
  }
403
404
 
404
405
  union_members:
@@ -164,11 +164,15 @@ module GraphQL
164
164
  def print_object_type_definition(object_type)
165
165
  out = print_description(object_type)
166
166
  out << "type #{object_type.name}"
167
- out << " implements " << object_type.interfaces.map(&:name).join(" & ") unless object_type.interfaces.empty?
167
+ out << print_implements(object_type) unless object_type.interfaces.empty?
168
168
  out << print_directives(object_type.directives)
169
169
  out << print_field_definitions(object_type.fields)
170
170
  end
171
171
 
172
+ def print_implements(type)
173
+ " implements #{type.interfaces.map(&:name).join(" & ")}"
174
+ end
175
+
172
176
  def print_input_value_definition(input_value)
173
177
  out = "#{input_value.name}: #{print_node(input_value.type)}".dup
174
178
  out << " = #{print_node(input_value.default_value)}" unless input_value.default_value.nil?
@@ -200,6 +204,7 @@ module GraphQL
200
204
  def print_interface_type_definition(interface_type)
201
205
  out = print_description(interface_type)
202
206
  out << "interface #{interface_type.name}"
207
+ out << print_implements(interface_type) if interface_type.interfaces.any?
203
208
  out << print_directives(interface_type.directives)
204
209
  out << print_field_definitions(interface_type.fields)
205
210
  end
@@ -79,7 +79,7 @@ module GraphQL
79
79
 
80
80
  arg_owner = @current_input_type || @current_directive || @current_field
81
81
  old_current_argument = @current_argument
82
- @current_argument = arg_owner.arguments[argument.name]
82
+ @current_argument = arg_owner.get_argument(argument.name, @query.context)
83
83
 
84
84
  old_input_type = @current_input_type
85
85
  @current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type
@@ -113,7 +113,7 @@ module GraphQL
113
113
  end
114
114
 
115
115
  def print_field(field, indent: "")
116
- @current_field = query.schema.get_field(@current_type, field.name)
116
+ @current_field = query.get_field(@current_type, field.name)
117
117
  old_type = @current_type
118
118
  @current_type = @current_field.type.unwrap
119
119
  res = super
@@ -125,7 +125,7 @@ module GraphQL
125
125
  old_type = @current_type
126
126
 
127
127
  if inline_fragment.type
128
- @current_type = query.schema.types[inline_fragment.type.name]
128
+ @current_type = query.get_type(inline_fragment.type.name)
129
129
  end
130
130
 
131
131
  res = super
@@ -137,7 +137,7 @@ module GraphQL
137
137
 
138
138
  def print_fragment_definition(fragment_def, indent: "")
139
139
  old_type = @current_type
140
- @current_type = query.schema.types[fragment_def.type.name]
140
+ @current_type = query.get_type(fragment_def.type.name)
141
141
 
142
142
  res = super
143
143
 
@@ -193,7 +193,7 @@ module GraphQL
193
193
  end
194
194
 
195
195
  arguments = value.map do |key, val|
196
- sub_type = type.arguments[key.to_s].type
196
+ sub_type = type.get_argument(key.to_s, @query.context).type
197
197
 
198
198
  GraphQL::Language::Nodes::Argument.new(
199
199
  name: key.to_s,
@@ -4,10 +4,6 @@ module GraphQL
4
4
  # Emitted by the lexer and passed to the parser.
5
5
  # Contains type, value and position data.
6
6
  class Token
7
- if !String.method_defined?(:-@)
8
- using GraphQL::StringDedupBackport
9
- end
10
-
11
7
  # @return [Symbol] The kind of token this is
12
8
  attr_reader :name
13
9
  # @return [String] The text of this token
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  class NameValidator
4
- if !String.method_defined?(:match?)
5
- using GraphQL::StringMatchBackport
6
- end
7
-
8
4
  VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
9
5
 
10
6
  def self.validate!(name)
@@ -9,7 +9,7 @@ module GraphQL
9
9
  include GraphQL::Dig
10
10
 
11
11
  def self.construct_arguments_class(argument_owner)
12
- argument_definitions = argument_owner.arguments
12
+ argument_definitions = argument_owner.arguments # rubocop:disable Development/ContextIsPassedCop -- legacy-related
13
13
  argument_owner.arguments_class = Class.new(self) do
14
14
  self.argument_owner = argument_owner
15
15
  self.argument_definitions = argument_definitions
@@ -7,7 +7,7 @@ module GraphQL
7
7
  Hash.new do |h1, irep_or_ast_node|
8
8
  h1[irep_or_ast_node] = Hash.new do |h2, definition|
9
9
  ast_node = irep_or_ast_node.is_a?(GraphQL::InternalRepresentation::Node) ? irep_or_ast_node.ast_node : irep_or_ast_node
10
- h2[definition] = if definition.arguments.empty?
10
+ h2[definition] = if definition.arguments(query.context).empty?
11
11
  GraphQL::Query::Arguments::NO_ARGS
12
12
  else
13
13
  GraphQL::Query::LiteralInput.from_arguments(
@@ -157,7 +157,7 @@ module GraphQL
157
157
  end
158
158
 
159
159
  def dataloader
160
- @dataloader ||= query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new
160
+ @dataloader ||= self[:dataloader] || (query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new)
161
161
  end
162
162
 
163
163
  # @api private
@@ -223,9 +223,12 @@ module GraphQL
223
223
 
224
224
  # @return [GraphQL::Schema::Warden]
225
225
  def warden
226
- @warden ||= @query.warden
226
+ @warden ||= (@query && @query.warden)
227
227
  end
228
228
 
229
+ # @api private
230
+ attr_writer :warden
231
+
229
232
  # Get an isolated hash for `ns`. Doesn't affect user-provided storage.
230
233
  # @param ns [Object] a usage-specific namespace identifier
231
234
  # @return [Hash] namespaced storage
@@ -62,7 +62,7 @@ module GraphQL
62
62
  raise ArgumentError, "Unexpected ast_arguments: #{ast_arguments}"
63
63
  end
64
64
 
65
- argument_defns = argument_owner.arguments
65
+ argument_defns = argument_owner.arguments(context || GraphQL::Query::NullContext)
66
66
  argument_defns.each do |arg_name, arg_defn|
67
67
  ast_arg = indexed_arguments[arg_name]
68
68
  # First, check the argument in the AST.
@@ -4,9 +4,11 @@ module GraphQL
4
4
  # This object can be `ctx` in places where there is no query
5
5
  class NullContext
6
6
  class NullWarden < GraphQL::Schema::Warden
7
- def visible?(t); true; end
8
- def visible_field?(t); true; end
9
- def visible_type?(t); true; end
7
+ def visible_field?(field, ctx); true; end
8
+ def visible_argument?(arg, ctx); true; end
9
+ def visible_type?(type, ctx); true; end
10
+ def visible_enum_value?(ev, ctx); true; end
11
+ def visible_type_membership?(tm, ctx); true; end
10
12
  end
11
13
 
12
14
  class NullQuery
@@ -15,12 +17,15 @@ module GraphQL
15
17
  end
16
18
  end
17
19
 
20
+ class NullSchema < GraphQL::Schema
21
+ end
22
+
18
23
  attr_reader :schema, :query, :warden, :dataloader
19
24
 
20
25
  def initialize
21
26
  @query = NullQuery.new
22
27
  @dataloader = GraphQL::Dataloader::NullDataloader.new
23
- @schema = GraphQL::Schema.new
28
+ @schema = NullSchema
24
29
  @warden = NullWarden.new(
25
30
  GraphQL::Filter.new,
26
31
  context: self,
@@ -31,7 +36,7 @@ module GraphQL
31
36
  def [](key); end
32
37
 
33
38
  def interpreter?
34
- false
39
+ true
35
40
  end
36
41
 
37
42
  class << self
@@ -40,10 +45,10 @@ module GraphQL
40
45
  def [](key); end
41
46
 
42
47
  def instance
43
- @instance = self.new
48
+ @instance ||= self.new
44
49
  end
45
50
 
46
- def_delegators :instance, :query, :schema, :warden, :interpreter?, :dataloader
51
+ def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader
47
52
  end
48
53
  end
49
54
  end
@@ -81,7 +81,7 @@ module GraphQL
81
81
  # is added to the "errors" key.
82
82
  def get_raw_value
83
83
  begin
84
- @field_ctx.schema.middleware.invoke([parent_type, target, field, arguments, @field_ctx])
84
+ @field_ctx.schema.middleware.invoke([parent_type, target, field, arguments, @field_ctx]) # rubocop:disable Development/ContextIsPassedCop -- unrelated
85
85
  rescue GraphQL::ExecutionError => err
86
86
  err
87
87
  end
@@ -45,7 +45,11 @@ module GraphQL
45
45
  end
46
46
  elsif default_value != nil
47
47
  memo[variable_name] = if ctx.interpreter?
48
- default_value
48
+ if default_value.is_a?(Language::Nodes::NullValue)
49
+ nil
50
+ else
51
+ default_value
52
+ end
49
53
  else
50
54
  # Add the variable if it wasn't provided but it has a default value (including `null`)
51
55
  GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
@@ -16,7 +16,6 @@ module GraphQL
16
16
  end
17
17
  end
18
18
 
19
-
20
19
  class EdgesResolve
21
20
  def initialize(edge_class:, resolve:)
22
21
  @edge_class = edge_class
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ require "rubocop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ class BaseCop < RuboCop::Cop::Base
8
+ extend RuboCop::Cop::AutoCorrector
9
+
10
+ # Return the source of `send_node`, but without the keyword argument represented by `pair_node`
11
+ def source_without_keyword_argument(send_node, pair_node)
12
+ # work back to the preceeding comma
13
+ first_pos = pair_node.location.expression.begin_pos
14
+ end_pos = pair_node.location.expression.end_pos
15
+ node_source = send_node.source_range.source
16
+ node_first_pos = send_node.location.expression.begin_pos
17
+
18
+ relative_first_pos = first_pos - node_first_pos
19
+ relative_last_pos = end_pos - node_first_pos
20
+
21
+ begin_removal_pos = relative_first_pos
22
+ while node_source[begin_removal_pos] != ","
23
+ begin_removal_pos -= 1
24
+ if begin_removal_pos < 1
25
+ raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})"
26
+ end
27
+ end
28
+
29
+ end_removal_pos = relative_last_pos
30
+ cleaned_node_source = node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1]
31
+ cleaned_node_source
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require_relative "base_cop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ # Identify (and auto-correct) any field configuration which duplicates
8
+ # the default `null: true` property.
9
+ #
10
+ # `null: true` is default because nullable fields can always be converted
11
+ # to non-null fields (`null: false`) without a breaking change. (The opposite change, from `null: false`
12
+ # to `null: true`, change.)
13
+ #
14
+ # @example
15
+ # # Both of these define `name: String` in GraphQL:
16
+ #
17
+ # # bad
18
+ # field :name, String, null: true
19
+ #
20
+ # # good
21
+ # field :name, String
22
+ #
23
+ class DefaultNullTrue < BaseCop
24
+ MSG = "`null: true` is the default and can be removed."
25
+
26
+ def_node_matcher :field_config_with_null_true?, <<-Pattern
27
+ (
28
+ send nil? :field ... (hash $(pair (sym :null) (true)) ...)
29
+ )
30
+ Pattern
31
+
32
+ def on_send(node)
33
+ field_config_with_null_true?(node) do |null_config|
34
+ add_offense(null_config) do |corrector|
35
+ cleaned_node_source = source_without_keyword_argument(node, null_config)
36
+ corrector.replace(node.source_range, cleaned_node_source)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require_relative "./base_cop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ # Identify (and auto-correct) any argument configuration which duplicates
8
+ # the default `required: true` property.
9
+ #
10
+ # `required: true` is default because required arguments can always be converted
11
+ # to optional arguments (`required: false`) without a breaking change. (The opposite change, from `required: false`
12
+ # to `required: true`, change.)
13
+ #
14
+ # @example
15
+ # # Both of these define `id: ID!` in GraphQL:
16
+ #
17
+ # # bad
18
+ # argument :id, ID, required: true
19
+ #
20
+ # # good
21
+ # argument :id, ID
22
+ #
23
+ class DefaultRequiredTrue < BaseCop
24
+ MSG = "`required: true` is the default and can be removed."
25
+
26
+ def_node_matcher :argument_config_with_required_true?, <<-Pattern
27
+ (
28
+ send nil? :argument ... (hash <$(pair (sym :required) (true)) ...>)
29
+ )
30
+ Pattern
31
+
32
+ def on_send(node)
33
+ argument_config_with_required_true?(node) do |required_config|
34
+ add_offense(required_config) do |corrector|
35
+ cleaned_node_source = source_without_keyword_argument(node, required_config)
36
+ corrector.replace(node, cleaned_node_source)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/rubocop/graphql/default_null_true"
4
+ require "graphql/rubocop/graphql/default_required_true"
@@ -24,7 +24,13 @@ module GraphQL
24
24
  end
25
25
 
26
26
  def get_type(name)
27
- @types[name] || @schema.get_type(name)
27
+ local_type = @types[name]
28
+ # This isn't really sophisticated, but
29
+ # I think it's good enough to support the current usage of LateBoundTypes
30
+ if local_type.is_a?(Array)
31
+ local_type = local_type.first
32
+ end
33
+ local_type || @schema.get_type(name)
28
34
  end
29
35
 
30
36
  # Lookup using `own_types` here because it's ok to override
@@ -77,13 +83,13 @@ module GraphQL
77
83
 
78
84
  def update_type_owner(owner, type)
79
85
  case owner
80
- when Class
86
+ when Module
81
87
  if owner.kind.union?
82
88
  # It's a union with possible_types
83
89
  # Replace the item by class name
84
90
  owner.assign_type_membership_object_type(type)
85
91
  @possible_types[owner.graphql_name] = owner.possible_types
86
- elsif type.kind.interface? && owner.kind.object?
92
+ elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
87
93
  new_interfaces = []
88
94
  owner.interfaces.each do |int_t|
89
95
  if int_t.is_a?(String) && int_t == type.graphql_name
@@ -98,12 +104,11 @@ module GraphQL
98
104
  owner.implements(*new_interfaces)
99
105
  new_interfaces.each do |int|
100
106
  pt = @possible_types[int.graphql_name] ||= []
101
- if !pt.include?(owner)
107
+ if !pt.include?(owner) && owner.is_a?(Class)
102
108
  pt << owner
103
109
  end
104
110
  end
105
111
  end
106
-
107
112
  when nil
108
113
  # It's a root type
109
114
  @types[type.graphql_name] = type
@@ -148,42 +153,42 @@ module GraphQL
148
153
  um << owner
149
154
  end
150
155
 
151
- if (prev_type = get_local_type(type.graphql_name))
152
- if prev_type != type
153
- raise DuplicateTypeNamesError.new(
154
- type_name: type.graphql_name,
155
- first_definition: prev_type,
156
- second_definition: type,
157
- path: path,
158
- )
159
- else
160
- # This type was already added
161
- end
156
+ if (prev_type = get_local_type(type.graphql_name)) && prev_type == type
157
+ # No need to re-visit
162
158
  elsif type.is_a?(Class) && type < GraphQL::Schema::Directive
163
159
  @directives << type
164
- type.arguments.each do |name, arg|
160
+ type.all_argument_definitions.each do |arg|
165
161
  arg_type = arg.type.unwrap
166
162
  references_to(arg_type, from: arg)
167
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [name])
163
+ add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
168
164
  if arg.default_value?
169
165
  @arguments_with_default_values << arg
170
166
  end
171
167
  end
172
168
  else
173
- @types[type.graphql_name] = type
169
+ prev_type = @types[type.graphql_name]
170
+ if prev_type.nil?
171
+ @types[type.graphql_name] = type
172
+ elsif prev_type.is_a?(Array)
173
+ prev_type << type
174
+ else
175
+ @types[type.graphql_name] = [prev_type, type]
176
+ end
177
+
174
178
  add_directives_from(type)
175
179
  if type.kind.fields?
176
- type.fields.each do |name, field|
180
+ type.all_field_definitions.each do |field|
181
+ name = field.graphql_name
177
182
  field_type = field.type.unwrap
178
183
  references_to(field_type, from: field)
179
184
  field_path = path + [name]
180
185
  add_type(field_type, owner: field, late_types: late_types, path: field_path)
181
186
  add_directives_from(field)
182
- field.arguments.each do |arg_name, arg|
187
+ field.all_argument_definitions.each do |arg|
183
188
  add_directives_from(arg)
184
189
  arg_type = arg.type.unwrap
185
190
  references_to(arg_type, from: arg)
186
- add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg_name])
191
+ add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg.graphql_name])
187
192
  if arg.default_value?
188
193
  @arguments_with_default_values << arg
189
194
  end
@@ -191,19 +196,19 @@ module GraphQL
191
196
  end
192
197
  end
193
198
  if type.kind.input_object?
194
- type.arguments.each do |arg_name, arg|
199
+ type.all_argument_definitions.each do |arg|
195
200
  add_directives_from(arg)
196
201
  arg_type = arg.type.unwrap
197
202
  references_to(arg_type, from: arg)
198
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg_name])
203
+ add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
199
204
  if arg.default_value?
200
205
  @arguments_with_default_values << arg
201
206
  end
202
207
  end
203
208
  end
204
209
  if type.kind.union?
205
- @possible_types[type.graphql_name] = type.possible_types
206
- type.possible_types.each do |t|
210
+ @possible_types[type.graphql_name] = type.all_possible_types
211
+ type.all_possible_types.each do |t|
207
212
  add_type(t, owner: type, late_types: late_types, path: path + ["possible_types"])
208
213
  end
209
214
  end
@@ -213,13 +218,17 @@ module GraphQL
213
218
  end
214
219
  end
215
220
  if type.kind.object?
216
- @possible_types[type.graphql_name] = [type]
221
+ possible_types_for_this_name = @possible_types[type.graphql_name] ||= []
222
+ possible_types_for_this_name << type
223
+ end
224
+
225
+ if type.kind.object? || type.kind.interface?
217
226
  type.interface_type_memberships.each do |interface_type_membership|
218
227
  case interface_type_membership
219
228
  when Schema::TypeMembership
220
229
  interface_type = interface_type_membership.abstract_type
221
230
  # We can get these now; we'll have to get late-bound types later
222
- if interface_type.is_a?(Module)
231
+ if interface_type.is_a?(Module) && type.is_a?(Class)
223
232
  implementers = @possible_types[interface_type.graphql_name] ||= []
224
233
  implementers << type
225
234
  end
@@ -2,10 +2,6 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class Argument
5
- if !String.method_defined?(:-@)
6
- using GraphQL::StringDedupBackport
7
- end
8
-
9
5
  include GraphQL::Schema::Member::CachedGraphQLDefinition
10
6
  include GraphQL::Schema::Member::AcceptsDefinition
11
7
  include GraphQL::Schema::Member::HasPath
@@ -52,10 +48,9 @@ module GraphQL
52
48
  # @param directives [Hash{Class => Hash}]
53
49
  # @param deprecation_reason [String]
54
50
  # @param validates [Hash, nil] Options for building validators, if any should be applied
55
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
51
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
56
52
  arg_name ||= name
57
53
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
58
- NameValidator.validate!(@name)
59
54
  @type_expr = type_expr || type
60
55
  @description = desc || description
61
56
  @null = !required
@@ -79,11 +74,18 @@ module GraphQL
79
74
  self.validates(validates)
80
75
 
81
76
  if definition_block
82
- # `self` will still be self, it will also be the first argument to the block:
83
- instance_exec(self, &definition_block)
77
+ if definition_block.arity == 1
78
+ instance_exec(self, &definition_block)
79
+ else
80
+ instance_eval(&definition_block)
81
+ end
84
82
  end
85
83
  end
86
84
 
85
+ def inspect
86
+ "#<#{self.class} #{path}: #{type.to_type_signature}#{description ? " @description=#{description.inspect}" : ""}>"
87
+ end
88
+
87
89
  # @return [Object] the value used when the client doesn't provide a value for this argument
88
90
  attr_reader :default_value
89
91
 
@@ -145,7 +147,7 @@ module GraphQL
145
147
  end
146
148
  end
147
149
  elsif as_type.kind.input_object?
148
- as_type.arguments.each do |_name, input_obj_arg|
150
+ as_type.arguments(ctx).each do |_name, input_obj_arg|
149
151
  input_obj_arg = input_obj_arg.type_class
150
152
  # TODO: this skips input objects whose values were alread replaced with application objects.
151
153
  # See: https://github.com/rmosolgo/graphql-ruby/issues/2633
@@ -3,12 +3,7 @@ require "graphql/schema/build_from_definition/resolve_map"
3
3
 
4
4
  module GraphQL
5
5
  class Schema
6
- # TODO Populate `.directive(...)` from here
7
6
  module BuildFromDefinition
8
- if !String.method_defined?(:-@)
9
- using GraphQL::StringDedupBackport
10
- end
11
-
12
7
  class << self
13
8
  # @see {Schema.from_definition}
14
9
  def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
@@ -394,6 +389,11 @@ module GraphQL
394
389
  include GraphQL::Schema::Interface
395
390
  graphql_name(interface_type_definition.name)
396
391
  description(interface_type_definition.description)
392
+ interface_type_definition.interfaces.each do |interface_name|
393
+ "Implements: #{interface_type_definition} -> #{interface_name}"
394
+ interface_defn = type_resolver.call(interface_name)
395
+ implements(interface_defn)
396
+ end
397
397
  ast_node(interface_type_definition)
398
398
  builder.build_directives(self, interface_type_definition, type_resolver)
399
399
 
@@ -426,18 +426,17 @@ module GraphQL
426
426
 
427
427
  # Don't do this for interfaces
428
428
  if default_resolve
429
- define_field_resolve_method(owner, resolve_method_name, field_definition.name)
429
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
430
+ # frozen_string_literal: true
431
+ def #{resolve_method_name}(**args)
432
+ field_instance = self.class.get_field("#{field_definition.name}")
433
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
434
+ end
435
+ RUBY
430
436
  end
431
437
  end
432
438
  end
433
439
 
434
- def define_field_resolve_method(owner, method_name, field_name)
435
- owner.define_method(method_name) { |**args|
436
- field_instance = self.class.get_field(field_name)
437
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
438
- }
439
- end
440
-
441
440
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
442
441
  resolve_type_proc = nil
443
442
  resolve_type_proc = ->(ast_node) {
@@ -42,7 +42,7 @@ module GraphQL
42
42
  GraphQL::Schema::Directive::INLINE_FRAGMENT
43
43
  )
44
44
 
45
- argument :flag, String, required: true,
45
+ argument :flag, String,
46
46
  description: "The name of the feature to check before continuing"
47
47
 
48
48
  # Implement the Directive API