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
@@ -19,13 +19,22 @@ module GraphQL
19
19
  # propagate null.
20
20
  null false
21
21
 
22
+ # @api private
22
23
  def initialize(object:, context:, field:)
23
24
  super
24
25
  # Figure out whether this is an update or an initial subscription
25
26
  @mode = context.query.subscription_update? ? :update : :subscribe
27
+ @subscription_written = false
28
+ @original_arguments = nil
29
+ if (subs_ns = context.namespace(:subscriptions)) &&
30
+ (sub_insts = subs_ns[:subscriptions])
31
+ sub_insts[context.current_path] = self
32
+ end
26
33
  end
27
34
 
35
+ # @api private
28
36
  def resolve_with_support(**args)
37
+ @original_arguments = args # before `loads:` have been run
29
38
  result = nil
30
39
  unsubscribed = true
31
40
  unsubscribed_result = catch :graphql_subscription_unsubscribed do
@@ -46,7 +55,9 @@ module GraphQL
46
55
  end
47
56
  end
48
57
 
49
- # Implement the {Resolve} API
58
+ # Implement the {Resolve} API.
59
+ # You can implement this if you want code to run for _both_ the initial subscription
60
+ # and for later updates. Or, implement {#subscribe} and {#update}
50
61
  def resolve(**args)
51
62
  # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
52
63
  # have an unexpected `@mode`
@@ -54,8 +65,9 @@ module GraphQL
54
65
  end
55
66
 
56
67
  # Wrap the user-defined `#subscribe` hook
68
+ # @api private
57
69
  def resolve_subscribe(**args)
58
- ret_val = args.any? ? subscribe(**args) : subscribe
70
+ ret_val = !args.empty? ? subscribe(**args) : subscribe
59
71
  if ret_val == :no_response
60
72
  context.skip
61
73
  else
@@ -71,8 +83,9 @@ module GraphQL
71
83
  end
72
84
 
73
85
  # Wrap the user-provided `#update` hook
86
+ # @api private
74
87
  def resolve_update(**args)
75
- ret_val = args.any? ? update(**args) : update
88
+ ret_val = !args.empty? ? update(**args) : update
76
89
  if ret_val == NO_UPDATE
77
90
  context.namespace(:subscriptions)[:no_update] = true
78
91
  context.skip
@@ -106,14 +119,13 @@ module GraphQL
106
119
  throw :graphql_subscription_unsubscribed, update_value
107
120
  end
108
121
 
109
- READING_SCOPE = ::Object.new
110
122
  # Call this method to provide a new subscription_scope; OR
111
123
  # call it without an argument to get the subscription_scope
112
124
  # @param new_scope [Symbol]
113
125
  # @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
114
126
  # @return [Symbol]
115
- def self.subscription_scope(new_scope = READING_SCOPE, optional: false)
116
- if new_scope != READING_SCOPE
127
+ def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false)
128
+ if new_scope != NOT_CONFIGURED
117
129
  @subscription_scope = new_scope
118
130
  @subscription_scope_optional = optional
119
131
  elsif defined?(@subscription_scope)
@@ -150,6 +162,40 @@ module GraphQL
150
162
  def self.topic_for(arguments:, field:, scope:)
151
163
  Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
152
164
  end
165
+
166
+ # Calls through to `schema.subscriptions` to register this subscription with the backend.
167
+ # This is automatically called by GraphQL-Ruby after a query finishes successfully,
168
+ # but if you need to commit the subscription during `#subscribe`, you can call it there.
169
+ # (This method also sets a flag showing that this subscription was already written.)
170
+ #
171
+ # If you call this method yourself, you may also need to {#unsubscribe}
172
+ # or call `subscriptions.delete_subscription` to clean up the database if the query crashes with an error
173
+ # later in execution.
174
+ # @return [void]
175
+ def write_subscription
176
+ if subscription_written?
177
+ raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`."
178
+ else
179
+ @subscription_written = true
180
+ context.schema.subscriptions.write_subscription(context.query, [event])
181
+ end
182
+ nil
183
+ end
184
+
185
+ # @return [Boolean] `true` if {#write_subscription} was called already
186
+ def subscription_written?
187
+ @subscription_written
188
+ end
189
+
190
+ # @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend
191
+ def event
192
+ @event ||= Subscriptions::Event.new(
193
+ name: field.name,
194
+ arguments: @original_arguments,
195
+ context: context,
196
+ field: field,
197
+ )
198
+ end
153
199
  end
154
200
  end
155
201
  end
@@ -11,7 +11,7 @@ module GraphQL
11
11
  end
12
12
 
13
13
  def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
14
- if types.any?
14
+ if !types.empty?
15
15
  types.each do |t|
16
16
  assert_valid_union_member(t)
17
17
  type_memberships << type_membership_class.new(self, t, **options)
@@ -51,19 +51,36 @@ module GraphQL
51
51
  end
52
52
 
53
53
  def validate(_object, context, value)
54
- matched_conditions = 0
54
+ fully_matched_conditions = 0
55
+ partially_matched_conditions = 0
55
56
 
56
57
  if !value.nil?
57
58
  @one_of.each do |one_of_condition|
58
59
  case one_of_condition
59
60
  when Symbol
60
61
  if value.key?(one_of_condition)
61
- matched_conditions += 1
62
+ fully_matched_conditions += 1
62
63
  end
63
64
  when Array
64
- if one_of_condition.all? { |k| value.key?(k) }
65
- matched_conditions += 1
66
- break
65
+ any_match = false
66
+ full_match = true
67
+
68
+ one_of_condition.each do |k|
69
+ if value.key?(k)
70
+ any_match = true
71
+ else
72
+ full_match = false
73
+ end
74
+ end
75
+
76
+ partial_match = !full_match && any_match
77
+
78
+ if full_match
79
+ fully_matched_conditions += 1
80
+ end
81
+
82
+ if partial_match
83
+ partially_matched_conditions += 1
67
84
  end
68
85
  else
69
86
  raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
@@ -71,7 +88,7 @@ module GraphQL
71
88
  end
72
89
  end
73
90
 
74
- if matched_conditions == 1
91
+ if fully_matched_conditions == 1 && partially_matched_conditions == 0
75
92
  nil # OK
76
93
  else
77
94
  @message || build_message(context)
@@ -143,7 +143,7 @@ module GraphQL
143
143
  end
144
144
  end
145
145
 
146
- if all_errors.any?
146
+ if !all_errors.empty?
147
147
  raise ValidationFailedError.new(errors: all_errors)
148
148
  end
149
149
  nil
@@ -112,6 +112,7 @@ module GraphQL
112
112
  :all_types_h,
113
113
  :fields,
114
114
  :loadable?,
115
+ :loadable_possible_types,
115
116
  :type,
116
117
  :arguments,
117
118
  :argument,
@@ -18,7 +18,7 @@ module GraphQL
18
18
  if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
19
19
  types
20
20
  else
21
- schema.visibility.profile_for(ctx, nil)
21
+ schema.visibility.profile_for(ctx)
22
22
  end
23
23
  end
24
24
 
@@ -38,38 +38,17 @@ module GraphQL
38
38
  @all_types = {}
39
39
  @all_types_loaded = false
40
40
  @unvisited_types = []
41
- @referenced_types = Hash.new { |h, type_defn| h[type_defn] = [] }.compare_by_identity
42
- @cached_directives = {}
43
41
  @all_directives = nil
44
- @cached_visible = Hash.new { |h, member|
45
- h[member] = @schema.visible?(member, @context)
46
- }.compare_by_identity
42
+ @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity
47
43
 
48
44
  @cached_visible_fields = Hash.new { |h, owner|
49
45
  h[owner] = Hash.new do |h2, field|
50
- h2[field] = if @cached_visible[field] &&
51
- (ret_type = field.type.unwrap) &&
52
- @cached_visible[ret_type] &&
53
- (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
54
-
55
- if !field.introspection?
56
- # The problem is that some introspection fields may have references
57
- # to non-custom introspection types.
58
- # If those were added here, they'd cause a DuplicateNamesError.
59
- # This is basically a bug -- those fields _should_ reference the custom types.
60
- add_type(ret_type, field)
61
- end
62
- true
63
- else
64
- false
65
- end
46
+ h2[field] = visible_field_for(owner, field)
66
47
  end.compare_by_identity
67
48
  }.compare_by_identity
68
49
 
69
50
  @cached_visible_arguments = Hash.new do |h, arg|
70
51
  h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
71
- add_type(arg_type, arg)
72
- arg.validate_default_value
73
52
  true
74
53
  else
75
54
  false
@@ -88,39 +67,7 @@ module GraphQL
88
67
  end
89
68
  end.compare_by_identity
90
69
 
91
- @cached_possible_types = Hash.new do |h, type|
92
- h[type] = case type.kind.name
93
- when "INTERFACE"
94
- load_all_types
95
- pts = []
96
- @unfiltered_interface_type_memberships[type].each { |itm|
97
- if @cached_visible[itm] && (ot = itm.object_type) && @cached_visible[ot] && referenced?(ot)
98
- pts << ot
99
- end
100
- }
101
- pts
102
- when "UNION"
103
- pts = []
104
- type.type_memberships.each { |tm|
105
- if @cached_visible[tm] &&
106
- (ot = tm.object_type) &&
107
- @cached_visible[ot] &&
108
- referenced?(ot)
109
- pts << ot
110
- end
111
- }
112
- pts
113
- when "OBJECT"
114
- load_all_types
115
- if @all_types[type.graphql_name] == type
116
- [type]
117
- else
118
- EmptyObjects::EMPTY_ARRAY
119
- end
120
- else
121
- GraphQL::EmptyObjects::EMPTY_ARRAY
122
- end
123
- end.compare_by_identity
70
+ @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity
124
71
 
125
72
  @cached_enum_values = Hash.new do |h, enum_t|
126
73
  values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
@@ -137,6 +84,8 @@ module GraphQL
137
84
  @cached_arguments = Hash.new do |h, owner|
138
85
  h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
139
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
140
89
  end
141
90
 
142
91
  def field_on_visible_interface?(field, owner)
@@ -164,17 +113,12 @@ module GraphQL
164
113
  end
165
114
 
166
115
  def type(type_name)
167
- t = if (loaded_t = @all_types[type_name])
168
- loaded_t
169
- elsif !@all_types_loaded
170
- load_all_types
171
- @all_types[type_name]
172
- end
116
+ t = @schema.visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
173
117
  if t
174
118
  if t.is_a?(Array)
175
119
  vis_t = nil
176
120
  t.each do |t_defn|
177
- if @cached_visible[t_defn]
121
+ if @cached_visible[t_defn] && referenced?(t_defn)
178
122
  if vis_t.nil?
179
123
  vis_t = t_defn
180
124
  else
@@ -184,7 +128,7 @@ module GraphQL
184
128
  end
185
129
  vis_t
186
130
  else
187
- if t && @cached_visible[t]
131
+ if t && @cached_visible[t] && referenced?(t)
188
132
  t
189
133
  else
190
134
  nil
@@ -215,7 +159,7 @@ module GraphQL
215
159
  end
216
160
  end
217
161
  end
218
- visible_f.ensure_loaded
162
+ visible_f&.ensure_loaded
219
163
  elsif f && @cached_visible_fields[owner][f.ensure_loaded]
220
164
  f
221
165
  else
@@ -267,15 +211,15 @@ module GraphQL
267
211
  end
268
212
 
269
213
  def query_root
270
- add_if_visible(@schema.query)
214
+ ((t = @schema.query) && @cached_visible[t]) ? t : nil
271
215
  end
272
216
 
273
217
  def mutation_root
274
- add_if_visible(@schema.mutation)
218
+ ((t = @schema.mutation) && @cached_visible[t]) ? t : nil
275
219
  end
276
220
 
277
221
  def subscription_root
278
- add_if_visible(@schema.subscription)
222
+ ((t = @schema.subscription) && @cached_visible[t]) ? t : nil
279
223
  end
280
224
 
281
225
  def all_types
@@ -293,38 +237,31 @@ module GraphQL
293
237
  end
294
238
 
295
239
  def directive_exists?(dir_name)
296
- if (dir = @schema.directives[dir_name]) && @cached_visible[dir]
297
- !!dir
298
- else
299
- load_all_types
300
- !!@cached_directives[dir_name]
301
- end
240
+ directives.any? { |d| d.graphql_name == dir_name }
302
241
  end
303
242
 
304
243
  def directives
305
- @all_directives ||= begin
306
- load_all_types
307
- dirs = []
308
- @schema.directives.each do |name, dir_defn|
309
- if !@cached_directives[name] && @cached_visible[dir_defn]
310
- dirs << dir_defn
311
- end
312
- end
313
- dirs.concat(@cached_directives.values)
314
- end
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
+ }
315
247
  end
316
248
 
317
249
  def loadable?(t, _ctx)
250
+ load_all_types
318
251
  !@all_types[t.graphql_name] && @cached_visible[t]
319
252
  end
320
253
 
254
+ def loadable_possible_types(t, _ctx)
255
+ @loadable_possible_types[t]
256
+ end
257
+
321
258
  def loaded_types
322
259
  @all_types.values
323
260
  end
324
261
 
325
- def reachable_type?(name)
262
+ def reachable_type?(type_name)
326
263
  load_all_types
327
- !!@all_types[name]
264
+ !!@all_types[type_name]
328
265
  end
329
266
 
330
267
  def visible_enum_value?(enum_value, _ctx = nil)
@@ -333,29 +270,6 @@ module GraphQL
333
270
 
334
271
  private
335
272
 
336
- def add_if_visible(t)
337
- (t && @cached_visible[t]) ? (add_type(t, true); t) : nil
338
- end
339
-
340
- def add_type(t, by_member)
341
- if t && @cached_visible[t]
342
- n = t.graphql_name
343
- if (prev_t = @all_types[n])
344
- if !prev_t.equal?(t)
345
- raise_duplicate_definition(prev_t, t)
346
- end
347
- false
348
- else
349
- @referenced_types[t] << by_member
350
- @all_types[n] = t
351
- @unvisited_types << t
352
- true
353
- end
354
- else
355
- false
356
- end
357
- end
358
-
359
273
  def non_duplicate_items(definitions, visibility_cache)
360
274
  non_dups = []
361
275
  definitions.each do |defn|
@@ -373,153 +287,71 @@ module GraphQL
373
287
  raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
374
288
  end
375
289
 
376
- def referenced?(t)
377
- load_all_types
378
- @referenced_types[t].any? { |reference| (reference == true) || @cached_visible[reference] }
379
- end
380
-
381
290
  def load_all_types
382
291
  return if @all_types_loaded
383
292
  @all_types_loaded = true
384
- entry_point_types = [
385
- query_root,
386
- mutation_root,
387
- subscription_root,
388
- *@schema.introspection_system.types.values,
389
- ]
390
-
391
- # Don't include any orphan_types whose interfaces aren't visible.
392
- @schema.orphan_types.each do |orphan_type|
393
- if @cached_visible[orphan_type] &&
394
- orphan_type.interface_type_memberships.any? { |tm| @cached_visible[tm] && @cached_visible[tm.abstract_type] }
395
- entry_point_types << orphan_type
396
- end
397
- end
398
-
399
- @schema.directives.each do |_dir_name, dir_class|
400
- if @cached_visible[dir_class]
401
- arguments(dir_class).each do |arg|
402
- entry_point_types << arg.type.unwrap
403
- end
404
- end
405
- end
406
-
407
- entry_point_types.compact! # Root types might be nil
408
- entry_point_types.flatten! # handle multiple defns
409
- entry_point_types.each { |t| add_type(t, true) }
410
-
411
- @unfiltered_interface_type_memberships = Hash.new { |h, k| h[k] = [] }.compare_by_identity
412
- @add_possible_types = Set.new
413
- @late_types = []
414
-
415
- while @unvisited_types.any? || @late_types.any?
416
- while t = @unvisited_types.pop
417
- # These have already been checked for `.visible?`
418
- visit_type(t)
419
- end
420
- @add_possible_types.each do |int_t|
421
- itms = @unfiltered_interface_type_memberships[int_t]
422
- itms.each do |itm|
423
- if @cached_visible[itm] && (obj_type = itm.object_type) && @cached_visible[obj_type]
424
- add_type(obj_type, itm)
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)
425
299
  end
300
+ @all_types[type_name] = member
301
+ true
302
+ else
303
+ false
426
304
  end
427
- end
428
- @add_possible_types.clear
429
-
430
- while (union_tm = @late_types.shift)
431
- late_obj_t = union_tm.object_type
432
- obj_t = @all_types[late_obj_t.graphql_name] || raise("Failed to resolve #{late_obj_t.graphql_name.inspect} from #{union_tm.inspect}")
433
- union_tm.abstract_type.assign_type_membership_object_type(obj_t)
305
+ else
306
+ @cached_visible[member]
434
307
  end
435
308
  end
436
-
309
+ visit.visit_each
437
310
  @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
438
311
  nil
439
312
  end
440
313
 
441
- def visit_type(type)
442
- visit_directives(type)
443
- case type.kind.name
444
- when "OBJECT", "INTERFACE"
445
- if type.kind.object?
446
- type.interface_type_memberships.each do |itm|
447
- @unfiltered_interface_type_memberships[itm.abstract_type] << itm
448
- end
449
- # recurse into visible implemented interfaces
450
- interfaces(type).each do |interface|
451
- add_type(interface, type)
452
- end
453
- else
454
- type.orphan_types.each { |t| add_type(t, type)}
455
- end
456
-
457
- # recurse into visible fields
458
- t_f = type.all_field_definitions
459
- t_f.each do |field|
460
- field.ensure_loaded
461
- if @cached_visible[field]
462
- visit_directives(field)
463
- field_type = field.type.unwrap
464
- if field_type.kind.interface?
465
- @add_possible_types.add(field_type)
466
- end
467
- add_type(field_type, field)
314
+ def referenced?(type_defn)
315
+ @schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
316
+ end
468
317
 
469
- # recurse into visible arguments
470
- arguments(field).each do |argument|
471
- visit_directives(argument)
472
- add_type(argument.type.unwrap, argument)
473
- end
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, impl_type)|
323
+ if @cached_visible[itm] && @cached_visible[impl_type] && referenced?(impl_type)
324
+ pts << impl_type
474
325
  end
475
326
  end
476
- when "INPUT_OBJECT"
477
- # recurse into visible arguments
478
- arguments(type).each do |argument|
479
- visit_directives(argument)
480
- add_type(argument.type.unwrap, argument)
481
- end
327
+ pts
482
328
  when "UNION"
483
- # recurse into visible possible types
484
- type.type_memberships.each do |tm|
485
- if @cached_visible[tm]
486
- obj_t = tm.object_type
487
- if obj_t.is_a?(GraphQL::Schema::LateBoundType)
488
- @late_types << tm
489
- else
490
- if obj_t.is_a?(String)
491
- obj_t = Member::BuildType.constantize(obj_t)
492
- tm.object_type = obj_t
493
- end
494
- if @cached_visible[obj_t]
495
- add_type(obj_t, tm)
496
- end
497
- end
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
498
336
  end
337
+ }
338
+ pts
339
+ when "OBJECT"
340
+ if @cached_visible[type]
341
+ [type]
342
+ else
343
+ EmptyObjects::EMPTY_ARRAY
499
344
  end
500
- when "ENUM"
501
- enum_values(type).each do |val|
502
- visit_directives(val)
503
- end
504
- when "SCALAR"
505
- # pass
345
+ else
346
+ GraphQL::EmptyObjects::EMPTY_ARRAY
506
347
  end
507
348
  end
508
349
 
509
- def visit_directives(member)
510
- member.directives.each { |dir|
511
- dir_class = dir.class
512
- if @cached_visible[dir_class]
513
- dir_name = dir_class.graphql_name
514
- if (existing_dir = @cached_directives[dir_name])
515
- if existing_dir != dir_class
516
- raise ArgumentError, "Two directives for `@#{dir_name}`: #{existing_dir}, #{dir.class}"
517
- end
518
- else
519
- @cached_directives[dir.graphql_name] = dir_class
520
- end
521
- end
522
- }
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))
523
355
  end
524
356
  end
525
357
  end