graphql 2.3.7 → 2.4.5

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +46 -0
  3. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  4. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  5. data/lib/generators/graphql/type_generator.rb +1 -1
  6. data/lib/graphql/analysis/field_usage.rb +1 -1
  7. data/lib/graphql/analysis/query_complexity.rb +3 -3
  8. data/lib/graphql/analysis/visitor.rb +8 -7
  9. data/lib/graphql/analysis.rb +4 -4
  10. data/lib/graphql/autoload.rb +37 -0
  11. data/lib/graphql/current.rb +52 -0
  12. data/lib/graphql/dataloader/async_dataloader.rb +7 -6
  13. data/lib/graphql/dataloader/source.rb +7 -4
  14. data/lib/graphql/dataloader.rb +40 -19
  15. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  16. data/lib/graphql/execution/interpreter/resolve.rb +13 -9
  17. data/lib/graphql/execution/interpreter/runtime.rb +35 -31
  18. data/lib/graphql/execution/interpreter.rb +6 -4
  19. data/lib/graphql/execution/lookahead.rb +18 -11
  20. data/lib/graphql/introspection/directive_type.rb +1 -1
  21. data/lib/graphql/introspection/entry_points.rb +2 -2
  22. data/lib/graphql/introspection/field_type.rb +1 -1
  23. data/lib/graphql/introspection/schema_type.rb +6 -11
  24. data/lib/graphql/introspection/type_type.rb +5 -5
  25. data/lib/graphql/invalid_null_error.rb +1 -1
  26. data/lib/graphql/language/cache.rb +13 -0
  27. data/lib/graphql/language/comment.rb +18 -0
  28. data/lib/graphql/language/document_from_schema_definition.rb +62 -34
  29. data/lib/graphql/language/lexer.rb +18 -15
  30. data/lib/graphql/language/nodes.rb +24 -16
  31. data/lib/graphql/language/parser.rb +14 -1
  32. data/lib/graphql/language/printer.rb +31 -15
  33. data/lib/graphql/language/sanitized_printer.rb +1 -1
  34. data/lib/graphql/language.rb +6 -6
  35. data/lib/graphql/pagination/connection.rb +1 -1
  36. data/lib/graphql/query/context/scoped_context.rb +1 -1
  37. data/lib/graphql/query/context.rb +13 -6
  38. data/lib/graphql/query/null_context.rb +3 -5
  39. data/lib/graphql/query/variable_validation_error.rb +1 -1
  40. data/lib/graphql/query.rb +72 -18
  41. data/lib/graphql/railtie.rb +7 -0
  42. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  43. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  44. data/lib/graphql/rubocop.rb +2 -0
  45. data/lib/graphql/schema/addition.rb +2 -1
  46. data/lib/graphql/schema/always_visible.rb +6 -2
  47. data/lib/graphql/schema/argument.rb +14 -1
  48. data/lib/graphql/schema/build_from_definition.rb +9 -1
  49. data/lib/graphql/schema/directive/flagged.rb +2 -2
  50. data/lib/graphql/schema/directive.rb +1 -1
  51. data/lib/graphql/schema/enum.rb +71 -23
  52. data/lib/graphql/schema/enum_value.rb +10 -2
  53. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +102 -47
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +5 -2
  58. data/lib/graphql/schema/input_object.rb +90 -39
  59. data/lib/graphql/schema/interface.rb +22 -5
  60. data/lib/graphql/schema/introspection_system.rb +5 -16
  61. data/lib/graphql/schema/loader.rb +1 -1
  62. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  63. data/lib/graphql/schema/member/has_arguments.rb +25 -20
  64. data/lib/graphql/schema/member/has_directives.rb +3 -3
  65. data/lib/graphql/schema/member/has_fields.rb +26 -6
  66. data/lib/graphql/schema/member/has_interfaces.rb +4 -4
  67. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  68. data/lib/graphql/schema/member/has_validators.rb +1 -1
  69. data/lib/graphql/schema/object.rb +8 -0
  70. data/lib/graphql/schema/printer.rb +1 -0
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +12 -14
  73. data/lib/graphql/schema/subscription.rb +2 -2
  74. data/lib/graphql/schema/type_expression.rb +2 -2
  75. data/lib/graphql/schema/union.rb +1 -1
  76. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  77. data/lib/graphql/schema/validator/required_validator.rb +28 -4
  78. data/lib/graphql/schema/validator.rb +3 -1
  79. data/lib/graphql/schema/visibility/migration.rb +187 -0
  80. data/lib/graphql/schema/visibility/profile.rb +353 -0
  81. data/lib/graphql/schema/visibility/visit.rb +190 -0
  82. data/lib/graphql/schema/visibility.rb +294 -0
  83. data/lib/graphql/schema/warden.rb +166 -16
  84. data/lib/graphql/schema.rb +348 -94
  85. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  86. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  87. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  88. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  89. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  90. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  91. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  92. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  93. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
  94. data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
  95. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  96. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  97. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  98. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  99. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  102. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  103. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  104. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  105. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  106. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  107. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  108. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  109. data/lib/graphql/static_validation/validation_context.rb +18 -2
  110. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  111. data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
  112. data/lib/graphql/subscriptions/event.rb +1 -1
  113. data/lib/graphql/subscriptions.rb +6 -4
  114. data/lib/graphql/testing/helpers.rb +10 -6
  115. data/lib/graphql/tracing/notifications_trace.rb +2 -2
  116. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  117. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  118. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  119. data/lib/graphql/types.rb +18 -11
  120. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  121. data/lib/graphql/version.rb +1 -1
  122. data/lib/graphql.rb +81 -45
  123. metadata +31 -8
  124. data/lib/graphql/language/token.rb +0 -34
  125. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -0,0 +1,353 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Visibility
6
+ # This class filters the types, fields, arguments, enum values, and directives in a schema
7
+ # based on the given `context`.
8
+ #
9
+ # It's like {Warden}, but has some differences:
10
+ #
11
+ # - It doesn't use {Schema}'s top-level caches (eg {Schema.references_to}, {Schema.possible_types}, {Schema.types})
12
+ # - It doesn't hide Interface or Union types when all their possible types are hidden. (Instead, those types should implement `.visible?` to hide in that case.)
13
+ # - It checks `.visible?` on root introspection types
14
+ # - It can be used to cache profiles by name for re-use across queries
15
+ class Profile
16
+ # @return [Schema::Visibility::Profile]
17
+ def self.from_context(ctx, schema)
18
+ if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
19
+ types
20
+ else
21
+ schema.visibility.profile_for(ctx, nil)
22
+ end
23
+ end
24
+
25
+ def self.null_profile(context:, schema:)
26
+ profile = self.new(name: "NullProfile", context: context, schema: schema)
27
+ profile.instance_variable_set(:@cached_visible, Hash.new { |k, v| k[v] = true }.compare_by_identity)
28
+ profile
29
+ end
30
+
31
+ # @return [Symbol, nil]
32
+ attr_reader :name
33
+
34
+ def initialize(name: nil, context:, schema:)
35
+ @name = name
36
+ @context = context
37
+ @schema = schema
38
+ @all_types = {}
39
+ @all_types_loaded = false
40
+ @unvisited_types = []
41
+ @all_directives = nil
42
+ @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity
43
+
44
+ @cached_visible_fields = Hash.new { |h, owner|
45
+ h[owner] = Hash.new do |h2, field|
46
+ h2[field] = visible_field_for(owner, field)
47
+ end.compare_by_identity
48
+ }.compare_by_identity
49
+
50
+ @cached_visible_arguments = Hash.new do |h, arg|
51
+ h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
52
+ true
53
+ else
54
+ false
55
+ end
56
+ end.compare_by_identity
57
+
58
+ @cached_parent_fields = Hash.new do |h, type|
59
+ h[type] = Hash.new do |h2, field_name|
60
+ h2[field_name] = type.get_field(field_name, @context)
61
+ end
62
+ end.compare_by_identity
63
+
64
+ @cached_parent_arguments = Hash.new do |h, arg_owner|
65
+ h[arg_owner] = Hash.new do |h2, arg_name|
66
+ h2[arg_name] = arg_owner.get_argument(arg_name, @context)
67
+ end
68
+ end.compare_by_identity
69
+
70
+ @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity
71
+
72
+ @cached_enum_values = Hash.new do |h, enum_t|
73
+ values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
74
+ if values.size == 0
75
+ raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t)
76
+ end
77
+ h[enum_t] = values
78
+ end.compare_by_identity
79
+
80
+ @cached_fields = Hash.new do |h, owner|
81
+ h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner])
82
+ end.compare_by_identity
83
+
84
+ @cached_arguments = Hash.new do |h, owner|
85
+ h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
86
+ end.compare_by_identity
87
+ end
88
+
89
+ def field_on_visible_interface?(field, owner)
90
+ ints = owner.interface_type_memberships.map(&:abstract_type)
91
+ field_name = field.graphql_name
92
+ filtered_ints = interfaces(owner)
93
+ any_interface_has_field = false
94
+ any_interface_has_visible_field = false
95
+ ints.each do |int_t|
96
+ if (_int_f_defn = @cached_parent_fields[int_t][field_name])
97
+ any_interface_has_field = true
98
+
99
+ if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
100
+ any_interface_has_visible_field = true
101
+ break
102
+ end
103
+ end
104
+ end
105
+
106
+ if any_interface_has_field
107
+ any_interface_has_visible_field
108
+ else
109
+ true
110
+ end
111
+ end
112
+
113
+ def type(type_name)
114
+ t = @schema.visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
115
+ if t
116
+ if t.is_a?(Array)
117
+ vis_t = nil
118
+ t.each do |t_defn|
119
+ if @cached_visible[t_defn] && referenced?(t_defn)
120
+ if vis_t.nil?
121
+ vis_t = t_defn
122
+ else
123
+ raise_duplicate_definition(vis_t, t_defn)
124
+ end
125
+ end
126
+ end
127
+ vis_t
128
+ else
129
+ if t && @cached_visible[t] && referenced?(t)
130
+ t
131
+ else
132
+ nil
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def field(owner, field_name)
139
+ f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
140
+ field
141
+ elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
142
+ entry_point_field
143
+ elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
144
+ dynamic_field
145
+ else
146
+ nil
147
+ end
148
+ if f.is_a?(Array)
149
+ visible_f = nil
150
+ f.each do |f_defn|
151
+ if @cached_visible_fields[owner][f_defn]
152
+
153
+ if visible_f.nil?
154
+ visible_f = f_defn
155
+ else
156
+ raise_duplicate_definition(visible_f, f_defn)
157
+ end
158
+ end
159
+ end
160
+ visible_f.ensure_loaded
161
+ elsif f && @cached_visible_fields[owner][f.ensure_loaded]
162
+ f
163
+ else
164
+ nil
165
+ end
166
+ end
167
+
168
+ def fields(owner)
169
+ @cached_fields[owner]
170
+ end
171
+
172
+ def arguments(owner)
173
+ @cached_arguments[owner]
174
+ end
175
+
176
+ def argument(owner, arg_name)
177
+ arg = @cached_parent_arguments[owner][arg_name]
178
+ if arg.is_a?(Array)
179
+ visible_arg = nil
180
+ arg.each do |arg_defn|
181
+ if @cached_visible_arguments[arg_defn]
182
+ if visible_arg.nil?
183
+ visible_arg = arg_defn
184
+ else
185
+ raise_duplicate_definition(visible_arg, arg_defn)
186
+ end
187
+ end
188
+ end
189
+ visible_arg
190
+ else
191
+ if arg && @cached_visible_arguments[arg]
192
+ arg
193
+ else
194
+ nil
195
+ end
196
+ end
197
+ end
198
+
199
+ def possible_types(type)
200
+ @cached_possible_types[type]
201
+ end
202
+
203
+ def interfaces(obj_or_int_type)
204
+ ints = obj_or_int_type.interface_type_memberships
205
+ .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
206
+ .map!(&:abstract_type)
207
+ ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
208
+ ints
209
+ end
210
+
211
+ def query_root
212
+ ((t = @schema.query) && @cached_visible[t]) ? t : nil
213
+ end
214
+
215
+ def mutation_root
216
+ ((t = @schema.mutation) && @cached_visible[t]) ? t : nil
217
+ end
218
+
219
+ def subscription_root
220
+ ((t = @schema.subscription) && @cached_visible[t]) ? t : nil
221
+ end
222
+
223
+ def all_types
224
+ load_all_types
225
+ @all_types.values
226
+ end
227
+
228
+ def all_types_h
229
+ load_all_types
230
+ @all_types
231
+ end
232
+
233
+ def enum_values(owner)
234
+ @cached_enum_values[owner]
235
+ end
236
+
237
+ def directive_exists?(dir_name)
238
+ directives.any? { |d| d.graphql_name == dir_name }
239
+ end
240
+
241
+ def directives
242
+ @all_directives ||= @schema.visibility.all_directives.select { |dir|
243
+ @cached_visible[dir] && @schema.visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
244
+ }
245
+ end
246
+
247
+ def loadable?(t, _ctx)
248
+ load_all_types
249
+ !@all_types[t.graphql_name] && @cached_visible[t]
250
+ end
251
+
252
+ def loaded_types
253
+ @all_types.values
254
+ end
255
+
256
+ def reachable_type?(type_name)
257
+ load_all_types
258
+ !!@all_types[type_name]
259
+ end
260
+
261
+ def visible_enum_value?(enum_value, _ctx = nil)
262
+ @cached_visible[enum_value]
263
+ end
264
+
265
+ private
266
+
267
+ def non_duplicate_items(definitions, visibility_cache)
268
+ non_dups = []
269
+ definitions.each do |defn|
270
+ if visibility_cache[defn]
271
+ if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name })
272
+ raise_duplicate_definition(dup_defn, defn)
273
+ end
274
+ non_dups << defn
275
+ end
276
+ end
277
+ non_dups
278
+ end
279
+
280
+ def raise_duplicate_definition(first_defn, second_defn)
281
+ raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
282
+ end
283
+
284
+ def load_all_types
285
+ return if @all_types_loaded
286
+ @all_types_loaded = true
287
+ visit = Visibility::Visit.new(@schema) do |member|
288
+ if member.is_a?(Module) && member.respond_to?(:kind)
289
+ if @cached_visible[member]
290
+ type_name = member.graphql_name
291
+ if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
292
+ raise_duplicate_definition(member, prev_t)
293
+ end
294
+ @all_types[type_name] = member
295
+ true
296
+ else
297
+ false
298
+ end
299
+ else
300
+ @cached_visible[member]
301
+ end
302
+ end
303
+ visit.visit_each
304
+ @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
305
+ nil
306
+ end
307
+
308
+ def referenced?(type_defn)
309
+ @schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
310
+ end
311
+
312
+ def possible_types_for(type)
313
+ case type.kind.name
314
+ when "INTERFACE"
315
+ pts = []
316
+ @schema.visibility.all_interface_type_memberships[type].each do |itm|
317
+ if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
318
+ pts << ot
319
+ end
320
+ end
321
+ pts
322
+ when "UNION"
323
+ pts = []
324
+ type.type_memberships.each { |tm|
325
+ if @cached_visible[tm] &&
326
+ (ot = tm.object_type) &&
327
+ @cached_visible[ot] &&
328
+ referenced?(ot)
329
+ pts << ot
330
+ end
331
+ }
332
+ pts
333
+ when "OBJECT"
334
+ if @cached_visible[type]
335
+ [type]
336
+ else
337
+ EmptyObjects::EMPTY_ARRAY
338
+ end
339
+ else
340
+ GraphQL::EmptyObjects::EMPTY_ARRAY
341
+ end
342
+ end
343
+
344
+ def visible_field_for(owner, field)
345
+ @cached_visible[field] &&
346
+ (ret_type = field.type.unwrap) &&
347
+ @cached_visible[ret_type] &&
348
+ (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
349
+ end
350
+ end
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Visibility
5
+ class Visit
6
+ def initialize(schema, &visit_block)
7
+ @schema = schema
8
+ @late_bound_types = nil
9
+ @unvisited_types = nil
10
+ # These accumulate between calls to prevent re-visiting the same types
11
+ @visited_types = Set.new.compare_by_identity
12
+ @visited_directives = Set.new.compare_by_identity
13
+ @visit_block = visit_block
14
+ end
15
+
16
+ def entry_point_types
17
+ ept = [
18
+ @schema.query,
19
+ @schema.mutation,
20
+ @schema.subscription,
21
+ *@schema.introspection_system.types.values,
22
+ *@schema.orphan_types,
23
+ ]
24
+ ept.compact!
25
+ ept
26
+ end
27
+
28
+ def entry_point_directives
29
+ @schema.directives.values
30
+ end
31
+
32
+ def visit_each(types: entry_point_types, directives: entry_point_directives)
33
+ @unvisited_types && raise("Can't re-enter `visit_each` on this Visit (another visit is already in progress)")
34
+ @unvisited_types = types
35
+ @late_bound_types = []
36
+ directives_to_visit = directives
37
+
38
+ while !@unvisited_types.empty? || !@late_bound_types.empty?
39
+ while (type = @unvisited_types.pop)
40
+ if @visited_types.add?(type) && @visit_block.call(type)
41
+ directives_to_visit.concat(type.directives)
42
+ case type.kind.name
43
+ when "OBJECT", "INTERFACE"
44
+ type.interface_type_memberships.each do |itm|
45
+ append_unvisited_type(type, itm.abstract_type)
46
+ end
47
+ if type.kind.interface?
48
+ type.orphan_types.each do |orphan_type|
49
+ append_unvisited_type(type, orphan_type)
50
+ end
51
+ end
52
+
53
+ type.all_field_definitions.each do |field|
54
+ field.ensure_loaded
55
+ if @visit_block.call(field)
56
+ directives_to_visit.concat(field.directives)
57
+ append_unvisited_type(type, field.type.unwrap)
58
+ field.all_argument_definitions.each do |argument|
59
+ if @visit_block.call(argument)
60
+ directives_to_visit.concat(argument.directives)
61
+ append_unvisited_type(field, argument.type.unwrap)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ when "INPUT_OBJECT"
67
+ type.all_argument_definitions.each do |argument|
68
+ if @visit_block.call(argument)
69
+ directives_to_visit.concat(argument.directives)
70
+ append_unvisited_type(type, argument.type.unwrap)
71
+ end
72
+ end
73
+ when "UNION"
74
+ type.type_memberships.each do |tm|
75
+ append_unvisited_type(type, tm.object_type)
76
+ end
77
+ when "ENUM"
78
+ type.all_enum_value_definitions.each do |val|
79
+ if @visit_block.call(val)
80
+ directives_to_visit.concat(val.directives)
81
+ end
82
+ end
83
+ when "SCALAR"
84
+ # pass -- nothing else to visit
85
+ else
86
+ raise "Invariant: unhandled type kind: #{type.kind.inspect}"
87
+ end
88
+ end
89
+ end
90
+
91
+ directives_to_visit.each do |dir|
92
+ dir_class = dir.is_a?(Class) ? dir : dir.class
93
+ if @visited_directives.add?(dir_class) && @visit_block.call(dir_class)
94
+ dir_class.all_argument_definitions.each do |arg_defn|
95
+ if @visit_block.call(arg_defn)
96
+ directives_to_visit.concat(arg_defn.directives)
97
+ append_unvisited_type(dir_class, arg_defn.type.unwrap)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ missed_late_types_streak = 0
104
+ while (owner, late_type = @late_bound_types.shift)
105
+ if (late_type.is_a?(String) && (type = Member::BuildType.constantize(type))) ||
106
+ (late_type.is_a?(LateBoundType) && (type = @visited_types.find { |t| t.graphql_name == late_type.graphql_name }))
107
+ missed_late_types_streak = 0 # might succeed next round
108
+ update_type_owner(owner, type)
109
+ append_unvisited_type(owner, type)
110
+ else
111
+ # Didn't find it -- keep trying
112
+ missed_late_types_streak += 1
113
+ @late_bound_types << [owner, late_type]
114
+ if missed_late_types_streak == @late_bound_types.size
115
+ raise UnresolvedLateBoundTypeError.new(type: late_type)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ @unvisited_types = nil
122
+ nil
123
+ end
124
+
125
+ private
126
+
127
+ def append_unvisited_type(owner, type)
128
+ if type.is_a?(LateBoundType) || type.is_a?(String)
129
+ @late_bound_types << [owner, type]
130
+ else
131
+ @unvisited_types << type
132
+ end
133
+ end
134
+
135
+ def update_type_owner(owner, type)
136
+ case owner
137
+ when Module
138
+ if owner.kind.union?
139
+ owner.assign_type_membership_object_type(type)
140
+ elsif type.kind.interface?
141
+ new_interfaces = []
142
+ owner.interfaces.each do |int_t|
143
+ if int_t.is_a?(String) && int_t == type.graphql_name
144
+ new_interfaces << type
145
+ elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name
146
+ new_interfaces << type
147
+ else
148
+ # Don't re-add proper interface definitions,
149
+ # they were probably already added, maybe with options.
150
+ end
151
+ end
152
+ owner.implements(*new_interfaces)
153
+ new_interfaces.each do |int|
154
+ pt = @possible_types[int] ||= []
155
+ if !pt.include?(owner) && owner.is_a?(Class)
156
+ pt << owner
157
+ end
158
+ int.interfaces.each do |indirect_int|
159
+ if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name)) # rubocop:disable Development/ContextIsPassedCop
160
+ update_type_owner(owner, indirect_int_type)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ when GraphQL::Schema::Argument, GraphQL::Schema::Field
166
+ orig_type = owner.type
167
+ # Apply list/non-null wrapper as needed
168
+ if orig_type.respond_to?(:of_type)
169
+ transforms = []
170
+ while (orig_type.respond_to?(:of_type))
171
+ if orig_type.kind.non_null?
172
+ transforms << :to_non_null_type
173
+ elsif orig_type.kind.list?
174
+ transforms << :to_list_type
175
+ else
176
+ raise "Invariant: :of_type isn't non-null or list"
177
+ end
178
+ orig_type = orig_type.of_type
179
+ end
180
+ transforms.reverse_each { |t| type = type.public_send(t) }
181
+ end
182
+ owner.type = type
183
+ else
184
+ raise "Unexpected update: #{owner.inspect} #{type.inspect}"
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end