graphql 2.4.3 → 2.5.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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +38 -41
  4. data/lib/graphql/analysis.rb +15 -12
  5. data/lib/graphql/autoload.rb +38 -0
  6. data/lib/graphql/backtrace/table.rb +118 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  10. data/lib/graphql/dashboard/installable.rb +22 -0
  11. data/lib/graphql/dashboard/limiters.rb +93 -0
  12. data/lib/graphql/dashboard/operation_store.rb +199 -0
  13. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  14. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  15. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  16. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  17. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  18. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  19. data/lib/graphql/dashboard/statics/icon.png +0 -0
  20. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  36. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  37. data/lib/graphql/dashboard.rb +158 -0
  38. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  39. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  40. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/source.rb +3 -3
  43. data/lib/graphql/dataloader.rb +43 -14
  44. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  45. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
  46. data/lib/graphql/execution/interpreter/runtime.rb +78 -41
  47. data/lib/graphql/execution/interpreter.rb +16 -7
  48. data/lib/graphql/execution/multiplex.rb +1 -5
  49. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  50. data/lib/graphql/invalid_name_error.rb +1 -1
  51. data/lib/graphql/invalid_null_error.rb +5 -15
  52. data/lib/graphql/language/cache.rb +13 -0
  53. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  54. data/lib/graphql/language/lexer.rb +11 -4
  55. data/lib/graphql/language/nodes.rb +3 -0
  56. data/lib/graphql/language/parser.rb +15 -8
  57. data/lib/graphql/language/printer.rb +8 -8
  58. data/lib/graphql/language/static_visitor.rb +37 -33
  59. data/lib/graphql/language/visitor.rb +59 -55
  60. data/lib/graphql/pagination/connection.rb +1 -1
  61. data/lib/graphql/query/context/scoped_context.rb +1 -1
  62. data/lib/graphql/query/context.rb +6 -5
  63. data/lib/graphql/query/variable_validation_error.rb +1 -1
  64. data/lib/graphql/query.rb +19 -23
  65. data/lib/graphql/railtie.rb +7 -0
  66. data/lib/graphql/schema/addition.rb +1 -1
  67. data/lib/graphql/schema/argument.rb +7 -8
  68. data/lib/graphql/schema/build_from_definition.rb +99 -53
  69. data/lib/graphql/schema/directive/flagged.rb +1 -1
  70. data/lib/graphql/schema/directive.rb +2 -2
  71. data/lib/graphql/schema/enum.rb +36 -1
  72. data/lib/graphql/schema/enum_value.rb +1 -1
  73. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  74. data/lib/graphql/schema/field.rb +12 -12
  75. data/lib/graphql/schema/field_extension.rb +1 -1
  76. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  77. data/lib/graphql/schema/input_object.rb +76 -39
  78. data/lib/graphql/schema/interface.rb +3 -2
  79. data/lib/graphql/schema/loader.rb +1 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  81. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  82. data/lib/graphql/schema/member/has_directives.rb +4 -4
  83. data/lib/graphql/schema/member/has_fields.rb +19 -1
  84. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  85. data/lib/graphql/schema/member/has_validators.rb +1 -1
  86. data/lib/graphql/schema/member/scoped.rb +1 -1
  87. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  88. data/lib/graphql/schema/member.rb +1 -0
  89. data/lib/graphql/schema/object.rb +25 -8
  90. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  91. data/lib/graphql/schema/resolver.rb +11 -10
  92. data/lib/graphql/schema/subscription.rb +52 -6
  93. data/lib/graphql/schema/union.rb +1 -1
  94. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  95. data/lib/graphql/schema/validator.rb +1 -1
  96. data/lib/graphql/schema/visibility/migration.rb +1 -0
  97. data/lib/graphql/schema/visibility/profile.rb +69 -237
  98. data/lib/graphql/schema/visibility/visit.rb +190 -0
  99. data/lib/graphql/schema/visibility.rb +169 -28
  100. data/lib/graphql/schema/warden.rb +18 -5
  101. data/lib/graphql/schema.rb +93 -44
  102. data/lib/graphql/static_validation/all_rules.rb +1 -1
  103. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  104. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  105. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  106. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  107. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  108. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  109. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  110. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  111. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  112. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  113. data/lib/graphql/static_validation/validation_context.rb +1 -0
  114. data/lib/graphql/static_validation/validator.rb +6 -1
  115. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  116. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  117. data/lib/graphql/subscriptions/event.rb +12 -1
  118. data/lib/graphql/subscriptions/serialize.rb +1 -1
  119. data/lib/graphql/subscriptions.rb +1 -1
  120. data/lib/graphql/testing/helpers.rb +7 -4
  121. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  122. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  123. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  124. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  125. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  126. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  127. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  128. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  129. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  130. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  131. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  132. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  133. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  134. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  135. data/lib/graphql/tracing/monitor_trace.rb +285 -0
  136. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  137. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  138. data/lib/graphql/tracing/notifications_trace.rb +182 -34
  139. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  140. data/lib/graphql/tracing/null_trace.rb +9 -0
  141. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  142. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  143. data/lib/graphql/tracing/perfetto_trace.rb +734 -0
  144. data/lib/graphql/tracing/platform_trace.rb +5 -0
  145. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  146. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  147. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  148. data/lib/graphql/tracing/scout_trace.rb +32 -55
  149. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  150. data/lib/graphql/tracing/sentry_trace.rb +63 -94
  151. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  152. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  153. data/lib/graphql/tracing/trace.rb +111 -1
  154. data/lib/graphql/tracing.rb +31 -30
  155. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  156. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  157. data/lib/graphql/types.rb +18 -11
  158. data/lib/graphql/version.rb +1 -1
  159. data/lib/graphql.rb +55 -47
  160. metadata +146 -11
  161. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  162. data/lib/graphql/backtrace/trace.rb +0 -93
  163. data/lib/graphql/backtrace/tracer.rb +0 -80
  164. data/lib/graphql/schema/null_mask.rb +0 -11
  165. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/schema/visibility/profile"
3
3
  require "graphql/schema/visibility/migration"
4
+ require "graphql/schema/visibility/visit"
4
5
 
5
6
  module GraphQL
6
7
  class Schema
@@ -12,41 +13,80 @@ module GraphQL
12
13
  # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
13
14
  # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
14
15
  def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
16
+ profiles&.each { |name, ctx|
17
+ ctx[:visibility_profile] = name
18
+ ctx.freeze
19
+ }
15
20
  schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
21
+ if preload
22
+ schema.visibility.preload
23
+ end
16
24
  end
17
25
 
18
26
  def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
19
27
  @schema = schema
20
28
  schema.use_visibility_profile = true
21
- if migration_errors
22
- schema.visibility_profile_class = Migration
29
+ schema.visibility_profile_class = if migration_errors
30
+ Visibility::Migration
31
+ else
32
+ Visibility::Profile
23
33
  end
24
34
  @preload = preload
25
35
  @profiles = profiles
26
36
  @cached_profiles = {}
27
37
  @dynamic = dynamic
28
38
  @migration_errors = migration_errors
29
- if preload
30
- # Traverse the schema now (and in the *_configured hooks below)
31
- # To make sure things are loaded during boot
32
- @preloaded_types = Set.new
33
- types_to_visit = [
34
- @schema.query,
35
- @schema.mutation,
36
- @schema.subscription,
37
- *@schema.introspection_system.types.values,
38
- *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
39
- *@schema.orphan_types,
40
- ]
41
- # Root types may have been nil:
42
- types_to_visit.compact!
43
- ensure_all_loaded(types_to_visit)
44
-
45
- profiles.each do |profile_name, example_ctx|
46
- example_ctx[:visibility_profile] = profile_name
47
- prof = profile_for(example_ctx, profile_name)
48
- prof.all_types # force loading
49
- end
39
+ # Top-level type caches:
40
+ @visit = nil
41
+ @interface_type_memberships = nil
42
+ @directives = nil
43
+ @types = nil
44
+ @all_references = nil
45
+ @loaded_all = false
46
+ end
47
+
48
+ def all_directives
49
+ load_all
50
+ @directives
51
+ end
52
+
53
+ def all_interface_type_memberships
54
+ load_all
55
+ @interface_type_memberships
56
+ end
57
+
58
+ def all_references
59
+ load_all
60
+ @all_references
61
+ end
62
+
63
+ def get_type(type_name)
64
+ load_all
65
+ @types[type_name]
66
+ end
67
+
68
+ def preload?
69
+ @preload
70
+ end
71
+
72
+ def preload
73
+ # Traverse the schema now (and in the *_configured hooks below)
74
+ # To make sure things are loaded during boot
75
+ @preloaded_types = Set.new
76
+ types_to_visit = [
77
+ @schema.query,
78
+ @schema.mutation,
79
+ @schema.subscription,
80
+ *@schema.introspection_system.types.values,
81
+ *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
82
+ *@schema.orphan_types,
83
+ ]
84
+ # Root types may have been nil:
85
+ types_to_visit.compact!
86
+ ensure_all_loaded(types_to_visit)
87
+ @profiles.each do |profile_name, example_ctx|
88
+ prof = profile_for(example_ctx)
89
+ prof.all_types # force loading
50
90
  end
51
91
  end
52
92
 
@@ -108,8 +148,8 @@ module GraphQL
108
148
 
109
149
  attr_reader :cached_profiles
110
150
 
111
- def profile_for(context, visibility_profile)
112
- if @profiles.any?
151
+ def profile_for(context, visibility_profile = context[:visibility_profile])
152
+ if !@profiles.empty?
113
153
  if visibility_profile.nil?
114
154
  if @dynamic
115
155
  if context.is_a?(Query::NullContext)
@@ -117,13 +157,14 @@ module GraphQL
117
157
  else
118
158
  @schema.visibility_profile_class.new(context: context, schema: @schema)
119
159
  end
120
- elsif @profiles.any?
160
+ elsif !@profiles.empty?
121
161
  raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
122
162
  end
123
163
  elsif !@profiles.include?(visibility_profile)
124
164
  raise ArgumentError, "`#{visibility_profile.inspect}` isn't allowed for `visibility_profile:` (must be one of #{@profiles.keys.map(&:inspect).join(", ")}). Or, add `#{visibility_profile.inspect}` to the list of profiles in the schema definition."
125
165
  else
126
- @cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: context, schema: @schema)
166
+ profile_ctx = @profiles[visibility_profile]
167
+ @cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
127
168
  end
128
169
  elsif context.is_a?(Query::NullContext)
129
170
  top_level_profile
@@ -132,7 +173,10 @@ module GraphQL
132
173
  end
133
174
  end
134
175
 
135
- private
176
+ attr_reader :top_level
177
+
178
+ # @api private
179
+ attr_reader :unfiltered_interface_type_memberships
136
180
 
137
181
  def top_level_profile(refresh: false)
138
182
  if refresh
@@ -141,6 +185,8 @@ module GraphQL
141
185
  @top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
142
186
  end
143
187
 
188
+ private
189
+
144
190
  def ensure_all_loaded(types_to_visit)
145
191
  while (type = types_to_visit.shift)
146
192
  if type.kind.fields? && @preloaded_types.add?(type)
@@ -153,6 +199,101 @@ module GraphQL
153
199
  top_level_profile(refresh: true)
154
200
  nil
155
201
  end
202
+
203
+ def load_all(types: nil)
204
+ if @visit.nil?
205
+ # Set up the visit system
206
+ @interface_type_memberships = Hash.new { |h, interface_type| h[interface_type] = [] }.compare_by_identity
207
+ @directives = []
208
+ @types = {} # String => Module
209
+ @all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity
210
+ @unions_for_references = Set.new
211
+ @visit = Visibility::Visit.new(@schema) do |member|
212
+ if member.is_a?(Module)
213
+ type_name = member.graphql_name
214
+ if (prev_t = @types[type_name])
215
+ if prev_t.is_a?(Array)
216
+ prev_t << member
217
+ else
218
+ @types[type_name] = [member, prev_t]
219
+ end
220
+ else
221
+ @types[member.graphql_name] = member
222
+ end
223
+ member.directives.each { |dir| @all_references[dir.class] << member }
224
+ if member < GraphQL::Schema::Directive
225
+ @directives << member
226
+ elsif member.respond_to?(:interface_type_memberships)
227
+ member.interface_type_memberships.each do |itm|
228
+ @all_references[itm.abstract_type] << member
229
+ # `itm.object_type` may not actually be `member` if this implementation
230
+ # is inherited from a superclass
231
+ @interface_type_memberships[itm.abstract_type] << [itm, member]
232
+ end
233
+ elsif member < GraphQL::Schema::Union
234
+ @unions_for_references << member
235
+ end
236
+ elsif member.is_a?(GraphQL::Schema::Argument)
237
+ member.validate_default_value
238
+ @all_references[member.type.unwrap] << member
239
+ if !(dirs = member.directives).empty?
240
+ dir_owner = member.owner
241
+ if dir_owner.respond_to?(:owner)
242
+ dir_owner = dir_owner.owner
243
+ end
244
+ dirs.each { |dir| @all_references[dir.class] << dir_owner }
245
+ end
246
+ elsif member.is_a?(GraphQL::Schema::Field)
247
+ @all_references[member.type.unwrap] << member
248
+ if !(dirs = member.directives).empty?
249
+ dir_owner = member.owner
250
+ dirs.each { |dir| @all_references[dir.class] << dir_owner }
251
+ end
252
+ elsif member.is_a?(GraphQL::Schema::EnumValue)
253
+ if !(dirs = member.directives).empty?
254
+ dir_owner = member.owner
255
+ dirs.each { |dir| @all_references[dir.class] << dir_owner }
256
+ end
257
+ end
258
+ true
259
+ end
260
+
261
+ @schema.root_types.each { |t| @all_references[t] << true }
262
+ @schema.introspection_system.types.each_value { |t| @all_references[t] << true }
263
+ @schema.directives.each_value { |dir_class| @all_references[dir_class] << true }
264
+
265
+ @visit.visit_each(types: []) # visit default directives
266
+ end
267
+
268
+ if types
269
+ @visit.visit_each(types: types, directives: [])
270
+ elsif @loaded_all == false
271
+ @loaded_all = true
272
+ @visit.visit_each
273
+ else
274
+ # already loaded all
275
+ return
276
+ end
277
+
278
+ # TODO: somehow don't iterate over all these,
279
+ # only the ones that may have been modified
280
+ @interface_type_memberships.each do |int_type, type_membership_pairs|
281
+ referers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
282
+ if !referers.empty?
283
+ type_membership_pairs.each do |(type_membership, impl_type)|
284
+ # Add new items only:
285
+ @all_references[impl_type] |= referers
286
+ end
287
+ end
288
+ end
289
+
290
+ @unions_for_references.each do |union_type|
291
+ refs = @all_references[union_type]
292
+ union_type.all_possible_types.each do |object_type|
293
+ @all_references[object_type] |= refs # Add new items
294
+ end
295
+ end
296
+ end
156
297
  end
157
298
  end
158
299
  end
@@ -72,6 +72,7 @@ module GraphQL
72
72
  def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
73
73
  def arguments(owner, ctx); owner.arguments(ctx); end
74
74
  def loadable?(type, ctx); type.visible?(ctx); end
75
+ def loadable_possible_types(type, ctx); type.possible_types(ctx); end
75
76
  def visibility_profile
76
77
  @visibility_profile ||= Warden::VisibilityProfile.new(self)
77
78
  end
@@ -106,6 +107,7 @@ module GraphQL
106
107
  def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
107
108
  def reachable_type?(type_name); true; end
108
109
  def loadable?(type, _ctx); true; end
110
+ def loadable_possible_types(union_type, _ctx); union_type.possible_types; end
109
111
  def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
110
112
  def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end
111
113
  def interfaces(obj_type); obj_type.interfaces; end
@@ -180,6 +182,10 @@ module GraphQL
180
182
  @warden.loadable?(t, ctx)
181
183
  end
182
184
 
185
+ def loadable_possible_types(t, ctx)
186
+ @warden.loadable_possible_types(t, ctx)
187
+ end
188
+
183
189
  def reachable_type?(type_name)
184
190
  !!@warden.reachable_type?(type_name)
185
191
  end
@@ -204,7 +210,7 @@ module GraphQL
204
210
  @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
205
211
  @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
206
212
  @visible_and_reachable_type = @unions = @unfiltered_interfaces =
207
- @reachable_type_set = @visibility_profile =
213
+ @reachable_type_set = @visibility_profile = @loadable_possible_types =
208
214
  nil
209
215
  @skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden }
210
216
  end
@@ -229,6 +235,13 @@ module GraphQL
229
235
  !reachable_type_set.include?(type) && visible_type?(type)
230
236
  end
231
237
 
238
+ def loadable_possible_types(union_type, _ctx)
239
+ @loadable_possible_types ||= read_through do |t|
240
+ t.possible_types # unfiltered
241
+ end
242
+ @loadable_possible_types[union_type]
243
+ end
244
+
232
245
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
233
246
  def get_type(type_name)
234
247
  @visible_types ||= read_through do |name|
@@ -297,7 +310,7 @@ module GraphQL
297
310
  def arguments(argument_owner, ctx = nil)
298
311
  @visible_arguments ||= read_through { |o|
299
312
  args = o.arguments(@context)
300
- if args.any?
313
+ if !args.empty?
301
314
  args = args.values
302
315
  args.select! { |a| visible_argument?(a, @context) }
303
316
  args
@@ -329,7 +342,7 @@ module GraphQL
329
342
  def interfaces(obj_type)
330
343
  @visible_interfaces ||= read_through { |t|
331
344
  ints = t.interfaces(@context)
332
- if ints.any?
345
+ if !ints.empty?
333
346
  ints.select! { |i| visible_type?(i) }
334
347
  end
335
348
  ints
@@ -389,9 +402,9 @@ module GraphQL
389
402
  next true if root_type?(type_defn) || type_defn.introspection?
390
403
 
391
404
  if type_defn.kind.union?
392
- possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
405
+ !possible_types(type_defn).empty? && (referenced?(type_defn) || orphan_type?(type_defn))
393
406
  elsif type_defn.kind.interface?
394
- if possible_types(type_defn).any?
407
+ if !possible_types(type_defn).empty?
395
408
  true
396
409
  else
397
410
  if @context.respond_to?(:logger) && (logger = @context.logger)