graphql 2.3.7 → 2.4.8

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 (152) 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 +38 -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 +9 -5
  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 +36 -23
  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 +52 -6
  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 +188 -0
  80. data/lib/graphql/schema/visibility/profile.rb +359 -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 +179 -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/default_subscription_resolve_extension.rb +12 -10
  113. data/lib/graphql/subscriptions/event.rb +13 -2
  114. data/lib/graphql/subscriptions/serialize.rb +2 -0
  115. data/lib/graphql/subscriptions.rb +6 -4
  116. data/lib/graphql/testing/helpers.rb +10 -6
  117. data/lib/graphql/tracing/active_support_notifications_trace.rb +1 -1
  118. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  119. data/lib/graphql/tracing/appoptics_trace.rb +2 -0
  120. data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
  121. data/lib/graphql/tracing/appsignal_trace.rb +2 -0
  122. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  123. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  124. data/lib/graphql/tracing/data_dog_trace.rb +2 -0
  125. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  126. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  127. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  128. data/lib/graphql/tracing/new_relic_trace.rb +2 -0
  129. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  130. data/lib/graphql/tracing/notifications_trace.rb +2 -2
  131. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  132. data/lib/graphql/tracing/null_trace.rb +9 -0
  133. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  134. data/lib/graphql/tracing/prometheus_trace.rb +5 -0
  135. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  136. data/lib/graphql/tracing/scout_trace.rb +2 -0
  137. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  138. data/lib/graphql/tracing/sentry_trace.rb +2 -0
  139. data/lib/graphql/tracing/statsd_trace.rb +2 -0
  140. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  141. data/lib/graphql/tracing/trace.rb +3 -0
  142. data/lib/graphql/tracing.rb +28 -30
  143. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  144. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  145. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  146. data/lib/graphql/types.rb +18 -11
  147. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  148. data/lib/graphql/version.rb +1 -1
  149. data/lib/graphql.rb +53 -45
  150. metadata +33 -8
  151. data/lib/graphql/language/token.rb +0 -34
  152. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -0,0 +1,359 @@
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
+
88
+ @loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
89
+ end
90
+
91
+ def field_on_visible_interface?(field, owner)
92
+ ints = owner.interface_type_memberships.map(&:abstract_type)
93
+ field_name = field.graphql_name
94
+ filtered_ints = interfaces(owner)
95
+ any_interface_has_field = false
96
+ any_interface_has_visible_field = false
97
+ ints.each do |int_t|
98
+ if (_int_f_defn = @cached_parent_fields[int_t][field_name])
99
+ any_interface_has_field = true
100
+
101
+ if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
102
+ any_interface_has_visible_field = true
103
+ break
104
+ end
105
+ end
106
+ end
107
+
108
+ if any_interface_has_field
109
+ any_interface_has_visible_field
110
+ else
111
+ true
112
+ end
113
+ end
114
+
115
+ def type(type_name)
116
+ t = @schema.visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
117
+ if t
118
+ if t.is_a?(Array)
119
+ vis_t = nil
120
+ t.each do |t_defn|
121
+ if @cached_visible[t_defn] && referenced?(t_defn)
122
+ if vis_t.nil?
123
+ vis_t = t_defn
124
+ else
125
+ raise_duplicate_definition(vis_t, t_defn)
126
+ end
127
+ end
128
+ end
129
+ vis_t
130
+ else
131
+ if t && @cached_visible[t] && referenced?(t)
132
+ t
133
+ else
134
+ nil
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ def field(owner, field_name)
141
+ f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
142
+ field
143
+ elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
144
+ entry_point_field
145
+ elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
146
+ dynamic_field
147
+ else
148
+ nil
149
+ end
150
+ if f.is_a?(Array)
151
+ visible_f = nil
152
+ f.each do |f_defn|
153
+ if @cached_visible_fields[owner][f_defn]
154
+
155
+ if visible_f.nil?
156
+ visible_f = f_defn
157
+ else
158
+ raise_duplicate_definition(visible_f, f_defn)
159
+ end
160
+ end
161
+ end
162
+ visible_f.ensure_loaded
163
+ elsif f && @cached_visible_fields[owner][f.ensure_loaded]
164
+ f
165
+ else
166
+ nil
167
+ end
168
+ end
169
+
170
+ def fields(owner)
171
+ @cached_fields[owner]
172
+ end
173
+
174
+ def arguments(owner)
175
+ @cached_arguments[owner]
176
+ end
177
+
178
+ def argument(owner, arg_name)
179
+ arg = @cached_parent_arguments[owner][arg_name]
180
+ if arg.is_a?(Array)
181
+ visible_arg = nil
182
+ arg.each do |arg_defn|
183
+ if @cached_visible_arguments[arg_defn]
184
+ if visible_arg.nil?
185
+ visible_arg = arg_defn
186
+ else
187
+ raise_duplicate_definition(visible_arg, arg_defn)
188
+ end
189
+ end
190
+ end
191
+ visible_arg
192
+ else
193
+ if arg && @cached_visible_arguments[arg]
194
+ arg
195
+ else
196
+ nil
197
+ end
198
+ end
199
+ end
200
+
201
+ def possible_types(type)
202
+ @cached_possible_types[type]
203
+ end
204
+
205
+ def interfaces(obj_or_int_type)
206
+ ints = obj_or_int_type.interface_type_memberships
207
+ .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
208
+ .map!(&:abstract_type)
209
+ ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
210
+ ints
211
+ end
212
+
213
+ def query_root
214
+ ((t = @schema.query) && @cached_visible[t]) ? t : nil
215
+ end
216
+
217
+ def mutation_root
218
+ ((t = @schema.mutation) && @cached_visible[t]) ? t : nil
219
+ end
220
+
221
+ def subscription_root
222
+ ((t = @schema.subscription) && @cached_visible[t]) ? t : nil
223
+ end
224
+
225
+ def all_types
226
+ load_all_types
227
+ @all_types.values
228
+ end
229
+
230
+ def all_types_h
231
+ load_all_types
232
+ @all_types
233
+ end
234
+
235
+ def enum_values(owner)
236
+ @cached_enum_values[owner]
237
+ end
238
+
239
+ def directive_exists?(dir_name)
240
+ directives.any? { |d| d.graphql_name == dir_name }
241
+ end
242
+
243
+ def directives
244
+ @all_directives ||= @schema.visibility.all_directives.select { |dir|
245
+ @cached_visible[dir] && @schema.visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
246
+ }
247
+ end
248
+
249
+ def loadable?(t, _ctx)
250
+ load_all_types
251
+ !@all_types[t.graphql_name] && @cached_visible[t]
252
+ end
253
+
254
+ def loadable_possible_types(t, _ctx)
255
+ @loadable_possible_types[t]
256
+ end
257
+
258
+ def loaded_types
259
+ @all_types.values
260
+ end
261
+
262
+ def reachable_type?(type_name)
263
+ load_all_types
264
+ !!@all_types[type_name]
265
+ end
266
+
267
+ def visible_enum_value?(enum_value, _ctx = nil)
268
+ @cached_visible[enum_value]
269
+ end
270
+
271
+ private
272
+
273
+ def non_duplicate_items(definitions, visibility_cache)
274
+ non_dups = []
275
+ definitions.each do |defn|
276
+ if visibility_cache[defn]
277
+ if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name })
278
+ raise_duplicate_definition(dup_defn, defn)
279
+ end
280
+ non_dups << defn
281
+ end
282
+ end
283
+ non_dups
284
+ end
285
+
286
+ def raise_duplicate_definition(first_defn, second_defn)
287
+ raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
288
+ end
289
+
290
+ def load_all_types
291
+ return if @all_types_loaded
292
+ @all_types_loaded = true
293
+ visit = Visibility::Visit.new(@schema) do |member|
294
+ if member.is_a?(Module) && member.respond_to?(:kind)
295
+ if @cached_visible[member]
296
+ type_name = member.graphql_name
297
+ if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
298
+ raise_duplicate_definition(member, prev_t)
299
+ end
300
+ @all_types[type_name] = member
301
+ true
302
+ else
303
+ false
304
+ end
305
+ else
306
+ @cached_visible[member]
307
+ end
308
+ end
309
+ visit.visit_each
310
+ @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
311
+ nil
312
+ end
313
+
314
+ def referenced?(type_defn)
315
+ @schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
316
+ end
317
+
318
+ def possible_types_for(type)
319
+ case type.kind.name
320
+ when "INTERFACE"
321
+ pts = []
322
+ @schema.visibility.all_interface_type_memberships[type].each do |itm|
323
+ if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
324
+ pts << ot
325
+ end
326
+ end
327
+ pts
328
+ when "UNION"
329
+ pts = []
330
+ type.type_memberships.each { |tm|
331
+ if @cached_visible[tm] &&
332
+ (ot = tm.object_type) &&
333
+ @cached_visible[ot] &&
334
+ referenced?(ot)
335
+ pts << ot
336
+ end
337
+ }
338
+ pts
339
+ when "OBJECT"
340
+ if @cached_visible[type]
341
+ [type]
342
+ else
343
+ EmptyObjects::EMPTY_ARRAY
344
+ end
345
+ else
346
+ GraphQL::EmptyObjects::EMPTY_ARRAY
347
+ end
348
+ end
349
+
350
+ def visible_field_for(owner, field)
351
+ @cached_visible[field] &&
352
+ (ret_type = field.type.unwrap) &&
353
+ @cached_visible[ret_type] &&
354
+ (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
355
+ end
356
+ end
357
+ end
358
+ end
359
+ 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