graphql 2.0.27 → 2.1.3

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  5. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  6. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  7. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  8. data/lib/generators/graphql/templates/base_field.erb +2 -0
  9. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  10. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  11. data/lib/generators/graphql/templates/base_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  13. data/lib/generators/graphql/templates/base_union.erb +2 -0
  14. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  15. data/lib/generators/graphql/templates/loader.erb +2 -0
  16. data/lib/generators/graphql/templates/mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/node_type.erb +2 -0
  18. data/lib/generators/graphql/templates/query_type.erb +2 -0
  19. data/lib/generators/graphql/templates/schema.erb +2 -0
  20. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  21. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  22. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  23. data/lib/graphql/analysis/ast.rb +15 -11
  24. data/lib/graphql/dataloader/source.rb +7 -0
  25. data/lib/graphql/dataloader.rb +38 -10
  26. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  27. data/lib/graphql/execution/interpreter/runtime.rb +95 -254
  28. data/lib/graphql/execution/interpreter.rb +0 -6
  29. data/lib/graphql/execution/lookahead.rb +1 -1
  30. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  31. data/lib/graphql/introspection/entry_points.rb +2 -2
  32. data/lib/graphql/language/block_string.rb +28 -16
  33. data/lib/graphql/language/definition_slice.rb +1 -1
  34. data/lib/graphql/language/document_from_schema_definition.rb +36 -35
  35. data/lib/graphql/language/nodes.rb +2 -2
  36. data/lib/graphql/language/printer.rb +294 -145
  37. data/lib/graphql/language/sanitized_printer.rb +20 -22
  38. data/lib/graphql/language/static_visitor.rb +167 -0
  39. data/lib/graphql/language/visitor.rb +20 -81
  40. data/lib/graphql/language.rb +1 -0
  41. data/lib/graphql/pagination/connection.rb +23 -1
  42. data/lib/graphql/query/context/scoped_context.rb +101 -0
  43. data/lib/graphql/query/context.rb +32 -98
  44. data/lib/graphql/query.rb +2 -19
  45. data/lib/graphql/rake_task.rb +3 -12
  46. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  47. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  48. data/lib/graphql/schema/field/scope_extension.rb +7 -1
  49. data/lib/graphql/schema/field.rb +7 -4
  50. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  51. data/lib/graphql/schema/introspection_system.rb +2 -0
  52. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  53. data/lib/graphql/schema/member/has_arguments.rb +19 -4
  54. data/lib/graphql/schema/member/has_fields.rb +4 -1
  55. data/lib/graphql/schema/member/has_interfaces.rb +21 -7
  56. data/lib/graphql/schema/member/scoped.rb +19 -0
  57. data/lib/graphql/schema/object.rb +8 -0
  58. data/lib/graphql/schema/printer.rb +8 -7
  59. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  60. data/lib/graphql/schema/resolver.rb +4 -0
  61. data/lib/graphql/schema/scalar.rb +3 -3
  62. data/lib/graphql/schema/subscription.rb +11 -4
  63. data/lib/graphql/schema/warden.rb +87 -89
  64. data/lib/graphql/schema.rb +125 -55
  65. data/lib/graphql/static_validation/all_rules.rb +1 -1
  66. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  67. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  68. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  69. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  70. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  71. data/lib/graphql/static_validation/validation_context.rb +5 -5
  72. data/lib/graphql/static_validation.rb +0 -1
  73. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
  74. data/lib/graphql/subscriptions.rb +11 -6
  75. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  76. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  77. data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
  78. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  79. data/lib/graphql/version.rb +1 -1
  80. data/lib/graphql.rb +1 -2
  81. metadata +23 -20
  82. data/lib/graphql/filter.rb +0 -59
  83. data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -30,6 +30,10 @@ module GraphQL
30
30
  # @see authorized_new to make instances
31
31
  protected :new
32
32
 
33
+ def wrap_scoped(object, context)
34
+ scoped_new(object, context)
35
+ end
36
+
33
37
  # This is called by the runtime to return an object to call methods on.
34
38
  def wrap(object, context)
35
39
  authorized_new(object, context)
@@ -91,6 +95,10 @@ module GraphQL
91
95
  end
92
96
  end
93
97
  end
98
+
99
+ def scoped_new(object, context)
100
+ self.new(object, context)
101
+ end
94
102
  end
95
103
 
96
104
  def initialize(object, context)
@@ -36,15 +36,11 @@ module GraphQL
36
36
 
37
37
  # @param schema [GraphQL::Schema]
38
38
  # @param context [Hash]
39
- # @param only [<#call(member, ctx)>]
40
- # @param except [<#call(member, ctx)>]
41
39
  # @param introspection [Boolean] Should include the introspection types in the string?
42
- def initialize(schema, context: nil, only: nil, except: nil, introspection: false)
40
+ def initialize(schema, context: nil, introspection: false)
43
41
  @document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new(
44
42
  schema,
45
43
  context: context,
46
- only: only,
47
- except: except,
48
44
  include_introspection_types: introspection,
49
45
  )
50
46
 
@@ -61,7 +57,12 @@ module GraphQL
61
57
  false
62
58
  end
63
59
  end
64
- schema = Class.new(GraphQL::Schema) { query(query_root) }
60
+ schema = Class.new(GraphQL::Schema) {
61
+ query(query_root)
62
+ def self.visible?(member, _ctx)
63
+ member.graphql_name != "Root"
64
+ end
65
+ }
65
66
 
66
67
  introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
67
68
  schema,
@@ -94,7 +95,7 @@ module GraphQL
94
95
 
95
96
  class IntrospectionPrinter < GraphQL::Language::Printer
96
97
  def print_schema_definition(schema)
97
- "schema {\n query: Root\n}"
98
+ print_string("schema {\n query: Root\n}")
98
99
  end
99
100
  end
100
101
  end
@@ -21,6 +21,10 @@ module GraphQL
21
21
  # @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
22
22
  #
23
23
  class RelayClassicMutation < GraphQL::Schema::Mutation
24
+ include GraphQL::Schema::HasSingleInputArgument
25
+
26
+ argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
27
+
24
28
  # The payload should always include this field
25
29
  field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
26
30
  # Relay classic default:
@@ -31,34 +35,14 @@ module GraphQL
31
35
  def resolve_with_support(**inputs)
32
36
  input = inputs[:input].to_kwargs
33
37
 
34
- new_extras = field ? field.extras : []
35
- all_extras = self.class.extras + new_extras
36
-
37
- # Transfer these from the top-level hash to the
38
- # shortcutted `input:` object
39
- all_extras.each do |ext|
40
- # It's possible that the `extra` was not passed along by this point,
41
- # don't re-add it if it wasn't given here.
42
- if inputs.key?(ext)
43
- input[ext] = inputs[ext]
44
- end
45
- end
46
-
47
38
  if input
48
39
  # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
49
40
  input_kwargs = input.to_h
50
41
  client_mutation_id = input_kwargs.delete(:client_mutation_id)
51
- else
52
- # Relay Classic Mutations with no `argument`s
53
- # don't require `input:`
54
- input_kwargs = {}
42
+ inputs[:input] = input_kwargs
55
43
  end
56
44
 
57
- return_value = if input_kwargs.any?
58
- super(**input_kwargs)
59
- else
60
- super()
61
- end
45
+ return_value = super(**inputs)
62
46
 
63
47
  context.query.after_lazy(return_value) do |return_hash|
64
48
  # It might be an error
@@ -68,112 +52,6 @@ module GraphQL
68
52
  return_hash
69
53
  end
70
54
  end
71
-
72
- class << self
73
- def dummy
74
- @dummy ||= begin
75
- d = Class.new(GraphQL::Schema::Resolver)
76
- d.argument_class(self.argument_class)
77
- # TODO make this lazier?
78
- d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
79
- d
80
- end
81
- end
82
-
83
- def field_arguments(context = GraphQL::Query::NullContext)
84
- dummy.arguments(context)
85
- end
86
-
87
- def get_field_argument(name, context = GraphQL::Query::NullContext)
88
- dummy.get_argument(name, context)
89
- end
90
-
91
- def own_field_arguments
92
- dummy.own_arguments
93
- end
94
-
95
- def all_field_argument_definitions
96
- dummy.all_argument_definitions
97
- end
98
-
99
- # Also apply this argument to the input type:
100
- def argument(*args, own_argument: false, **kwargs, &block)
101
- it = input_type # make sure any inherited arguments are already added to it
102
- arg = super(*args, **kwargs, &block)
103
-
104
- # This definition might be overriding something inherited;
105
- # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
106
- prev_args = it.own_arguments[arg.graphql_name]
107
- case prev_args
108
- when GraphQL::Schema::Argument
109
- if prev_args.owner != self
110
- it.own_arguments.delete(arg.graphql_name)
111
- end
112
- when Array
113
- prev_args.reject! { |a| a.owner != self }
114
- if prev_args.empty?
115
- it.own_arguments.delete(arg.graphql_name)
116
- end
117
- end
118
-
119
- it.add_argument(arg)
120
- arg
121
- end
122
-
123
- # The base class for generated input object types
124
- # @param new_class [Class] The base class to use for generating input object definitions
125
- # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
126
- def input_object_class(new_class = nil)
127
- if new_class
128
- @input_object_class = new_class
129
- end
130
- @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
131
- end
132
-
133
- # @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
134
- # @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
135
- def input_type(new_input_type = nil)
136
- if new_input_type
137
- @input_type = new_input_type
138
- end
139
- @input_type ||= generate_input_type
140
- end
141
-
142
- private
143
-
144
- # Generate the input type for the `input:` argument
145
- # To customize how input objects are generated, override this method
146
- # @return [Class] a subclass of {.input_object_class}
147
- def generate_input_type
148
- mutation_args = all_argument_definitions
149
- mutation_class = self
150
- Class.new(input_object_class) do
151
- class << self
152
- def default_graphql_name
153
- "#{self.mutation.graphql_name}Input"
154
- end
155
-
156
- def description(new_desc = nil)
157
- super || "Autogenerated input type of #{self.mutation.graphql_name}"
158
- end
159
- end
160
- mutation(mutation_class)
161
- # these might be inherited:
162
- mutation_args.each do |arg|
163
- add_argument(arg)
164
- end
165
- argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
166
- end
167
- end
168
- end
169
-
170
- private
171
-
172
- def authorize_arguments(args, values)
173
- # remove the `input` wrapper to match values
174
- input_args = args["input"].type.unwrap.arguments(context)
175
- super(input_args, values)
176
- end
177
55
  end
178
56
  end
179
57
  end
@@ -214,6 +214,10 @@ module GraphQL
214
214
  arguments(context)
215
215
  end
216
216
 
217
+ def any_field_arguments?
218
+ any_arguments?
219
+ end
220
+
217
221
  def get_field_argument(name, context = GraphQL::Query::NullContext)
218
222
  get_argument(name, context)
219
223
  end
@@ -19,9 +19,9 @@ module GraphQL
19
19
 
20
20
  def specified_by_url(new_url = nil)
21
21
  if new_url
22
- @specified_by_url = new_url
23
- elsif defined?(@specified_by_url)
24
- @specified_by_url
22
+ directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
23
+ elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
24
+ directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
25
25
  elsif superclass.respond_to?(:specified_by_url)
26
26
  superclass.specified_by_url
27
27
  else
@@ -28,14 +28,19 @@ module GraphQL
28
28
  def resolve_with_support(**args)
29
29
  result = nil
30
30
  unsubscribed = true
31
- catch :graphql_subscription_unsubscribed do
31
+ unsubscribed_result = catch :graphql_subscription_unsubscribed do
32
32
  result = super
33
33
  unsubscribed = false
34
34
  end
35
35
 
36
36
 
37
37
  if unsubscribed
38
- context.skip
38
+ if unsubscribed_result
39
+ context.namespace(:subscriptions)[:final_update] = true
40
+ unsubscribed_result
41
+ else
42
+ context.skip
43
+ end
39
44
  else
40
45
  result
41
46
  end
@@ -94,9 +99,11 @@ module GraphQL
94
99
  end
95
100
 
96
101
  # Call this to halt execution and remove this subscription from the system
97
- def unsubscribe
102
+ # @param update_value [Object] if present, deliver this update before unsubscribing
103
+ # @return [void]
104
+ def unsubscribe(update_value = nil)
98
105
  context.namespace(:subscriptions)[:unsubscribed] = true
99
- throw :graphql_subscription_unsubscribed
106
+ throw :graphql_subscription_unsubscribed, update_value
100
107
  end
101
108
 
102
109
  READING_SCOPE = ::Object.new
@@ -4,37 +4,12 @@ require 'set'
4
4
 
5
5
  module GraphQL
6
6
  class Schema
7
- # Restrict access to a {GraphQL::Schema} with a user-defined filter.
7
+ # Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations.
8
8
  #
9
9
  # When validating and executing a query, all access to schema members
10
10
  # should go through a warden. If you access the schema directly,
11
11
  # you may show a client something that it shouldn't be allowed to see.
12
12
  #
13
- # @example Hiding private fields
14
- # private_members = -> (member, ctx) { member.metadata[:private] }
15
- # result = Schema.execute(query_string, except: private_members)
16
- #
17
- # @example Custom filter implementation
18
- # # It must respond to `#call(member)`.
19
- # class MissingRequiredFlags
20
- # def initialize(user)
21
- # @user = user
22
- # end
23
- #
24
- # # Return `false` if any required flags are missing
25
- # def call(member, ctx)
26
- # member.metadata[:required_flags].any? do |flag|
27
- # !@user.has_flag?(flag)
28
- # end
29
- # end
30
- # end
31
- #
32
- # # Then, use the custom filter in query:
33
- # missing_required_flags = MissingRequiredFlags.new(current_user)
34
- #
35
- # # This query can only access members which match the user's flags
36
- # result = Schema.execute(query_string, except: missing_required_flags)
37
- #
38
13
  # @api private
39
14
  class Warden
40
15
  def self.from_context(context)
@@ -85,6 +60,7 @@ module GraphQL
85
60
  def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
86
61
  def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
87
62
  def arguments(owner, ctx); owner.arguments(ctx); end
63
+ def loadable?(type, ctx); type.visible?(ctx); end
88
64
  end
89
65
  end
90
66
 
@@ -109,27 +85,22 @@ module GraphQL
109
85
  def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
110
86
  def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
111
87
  def reachable_type?(type_name); true; end
88
+ def loadable?(type, _ctx); true; end
112
89
  def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
113
90
  def possible_types(type_defn); @schema.possible_types(type_defn); end
114
91
  def interfaces(obj_type); obj_type.interfaces; end
115
92
  end
116
93
 
117
- # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
118
94
  # @param context [GraphQL::Query::Context]
119
95
  # @param schema [GraphQL::Schema]
120
- def initialize(filter = nil, context:, schema:)
96
+ def initialize(context:, schema:)
121
97
  @schema = schema
122
98
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
123
99
  @query = @schema.query
124
100
  @mutation = @schema.mutation
125
101
  @subscription = @schema.subscription
126
102
  @context = context
127
- @visibility_cache = if filter
128
- read_through { |m| filter.call(m, context) }
129
- else
130
- read_through { |m| schema.visible?(m, context) }
131
- end
132
-
103
+ @visibility_cache = read_through { |m| schema.visible?(m, context) }
133
104
  @visibility_cache.compare_by_identity
134
105
  # Initialize all ivars to improve object shape consistency:
135
106
  @types = @visible_types = @reachable_types = @visible_parent_fields =
@@ -153,6 +124,11 @@ module GraphQL
153
124
  end
154
125
  end
155
126
 
127
+ # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
128
+ def loadable?(type, _ctx)
129
+ !reachable_type_set.include?(type) && visible_type?(type)
130
+ end
131
+
156
132
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
157
133
  def get_type(type_name)
158
134
  @visible_types ||= read_through do |name|
@@ -219,7 +195,16 @@ module GraphQL
219
195
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
220
196
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
221
197
  def arguments(argument_owner, ctx = nil)
222
- @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
198
+ @visible_arguments ||= read_through { |o|
199
+ args = o.arguments(@context)
200
+ if args.any?
201
+ args = args.values
202
+ args.select! { |a| visible_argument?(a, @context) }
203
+ args
204
+ else
205
+ EmptyObjects::EMPTY_ARRAY
206
+ end
207
+ }
223
208
  @visible_arguments[argument_owner]
224
209
  end
225
210
 
@@ -242,7 +227,13 @@ module GraphQL
242
227
 
243
228
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
244
229
  def interfaces(obj_type)
245
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
230
+ @visible_interfaces ||= read_through { |t|
231
+ ints = t.interfaces(@context)
232
+ if ints.any?
233
+ ints.select! { |i| visible_type?(i) }
234
+ end
235
+ ints
236
+ }
246
237
  @visible_interfaces[obj_type]
247
238
  end
248
239
 
@@ -298,11 +289,19 @@ module GraphQL
298
289
  next true if root_type?(type_defn) || type_defn.introspection?
299
290
 
300
291
  if type_defn.kind.union?
301
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
292
+ possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
302
293
  elsif type_defn.kind.interface?
303
- visible_possible_types?(type_defn)
294
+ possible_types(type_defn).any?
304
295
  else
305
- referenced?(type_defn) || visible_abstract_type?(type_defn)
296
+ if referenced?(type_defn)
297
+ true
298
+ elsif type_defn.kind.object?
299
+ # Show this object if it belongs to ...
300
+ interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
301
+ union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
302
+ else
303
+ false
304
+ end
306
305
  end
307
306
  end
308
307
 
@@ -369,23 +368,14 @@ module GraphQL
369
368
  @schema.orphan_types.include?(type_defn)
370
369
  end
371
370
 
372
- def visible_abstract_type?(type_defn)
373
- type_defn.kind.object? && (
374
- interfaces(type_defn).any? ||
375
- union_memberships(type_defn).any?
376
- )
377
- end
378
-
379
- def visible_possible_types?(type_defn)
380
- possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
381
- end
382
-
383
371
  def visible?(member)
384
372
  @visibility_cache[member]
385
373
  end
386
374
 
387
375
  def read_through
388
- Hash.new { |h, k| h[k] = yield(k) }
376
+ h = Hash.new { |h, k| h[k] = yield(k) }
377
+ h.compare_by_identity
378
+ h
389
379
  end
390
380
 
391
381
  def reachable_type_set
@@ -416,54 +406,62 @@ module GraphQL
416
406
  end
417
407
  end
418
408
 
409
+ included_interface_possible_types_set = Set.new
410
+
419
411
  until unvisited_types.empty?
420
412
  type = unvisited_types.pop
421
- if @reachable_type_set.add?(type)
422
- type_by_name = rt_hash[type.graphql_name] ||= type
423
- if type_by_name != type
424
- raise DuplicateNamesError.new(
425
- duplicated_name: type.graphql_name, duplicated_definition_1: type.inspect, duplicated_definition_2: type_by_name.inspect
426
- )
413
+ visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
414
+ end
415
+
416
+ @reachable_type_set
417
+ end
418
+
419
+ def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
420
+ if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
421
+ type_by_name = type_by_name_hash[type.graphql_name] ||= type
422
+ if type_by_name != type
423
+ name_1, name_2 = [type.inspect, type_by_name.inspect].sort
424
+ raise DuplicateNamesError.new(
425
+ duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
426
+ )
427
+ end
428
+ if type.kind.input_object?
429
+ # recurse into visible arguments
430
+ arguments(type).each do |argument|
431
+ argument_type = argument.type.unwrap
432
+ unvisited_types << argument_type
427
433
  end
428
- if type.kind.input_object?
429
- # recurse into visible arguments
430
- arguments(type).each do |argument|
431
- argument_type = argument.type.unwrap
432
- unvisited_types << argument_type
433
- end
434
- elsif type.kind.union?
435
- # recurse into visible possible types
436
- possible_types(type).each do |possible_type|
437
- unvisited_types << possible_type
434
+ elsif type.kind.union?
435
+ # recurse into visible possible types
436
+ possible_types(type).each do |possible_type|
437
+ unvisited_types << possible_type
438
+ end
439
+ elsif type.kind.fields?
440
+ if type.kind.object?
441
+ # recurse into visible implemented interfaces
442
+ interfaces(type).each do |interface|
443
+ unvisited_types << interface
438
444
  end
439
- elsif type.kind.fields?
440
- if type.kind.interface?
441
- # recurse into visible possible types
442
- possible_types(type).each do |possible_type|
443
- unvisited_types << possible_type
444
- end
445
- elsif type.kind.object?
446
- # recurse into visible implemented interfaces
447
- interfaces(type).each do |interface|
448
- unvisited_types << interface
449
- end
445
+ elsif include_interface_possible_types
446
+ possible_types(type).each do |pt|
447
+ unvisited_types << pt
450
448
  end
449
+ end
450
+ # Don't visit interface possible types -- it's not enough to justify visibility
451
451
 
452
- # recurse into visible fields
453
- fields(type).each do |field|
454
- field_type = field.type.unwrap
455
- unvisited_types << field_type
456
- # recurse into visible arguments
457
- arguments(field).each do |argument|
458
- argument_type = argument.type.unwrap
459
- unvisited_types << argument_type
460
- end
452
+ # recurse into visible fields
453
+ fields(type).each do |field|
454
+ field_type = field.type.unwrap
455
+ # In this case, if it's an interface, we want to include
456
+ visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
457
+ # recurse into visible arguments
458
+ arguments(field).each do |argument|
459
+ argument_type = argument.type.unwrap
460
+ unvisited_types << argument_type
461
461
  end
462
462
  end
463
463
  end
464
464
  end
465
-
466
- @reachable_type_set
467
465
  end
468
466
  end
469
467
  end