graphql 2.4.2 → 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.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +78 -41
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/lookahead.rb +8 -1
- data/lib/graphql/execution/multiplex.rb +1 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +5 -15
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +6 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +19 -23
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +12 -12
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +76 -39
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +11 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +69 -237
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +184 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +94 -45
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +285 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +63 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- 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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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,24 +148,43 @@ module GraphQL
|
|
|
108
148
|
|
|
109
149
|
attr_reader :cached_profiles
|
|
110
150
|
|
|
111
|
-
def profile_for(context, visibility_profile)
|
|
112
|
-
if
|
|
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
|
-
|
|
116
|
-
|
|
155
|
+
if context.is_a?(Query::NullContext)
|
|
156
|
+
top_level_profile
|
|
157
|
+
else
|
|
158
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
|
159
|
+
end
|
|
160
|
+
elsif !@profiles.empty?
|
|
117
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."
|
|
118
162
|
end
|
|
119
163
|
elsif !@profiles.include?(visibility_profile)
|
|
120
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."
|
|
121
165
|
else
|
|
122
|
-
@
|
|
166
|
+
profile_ctx = @profiles[visibility_profile]
|
|
167
|
+
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
|
|
123
168
|
end
|
|
169
|
+
elsif context.is_a?(Query::NullContext)
|
|
170
|
+
top_level_profile
|
|
124
171
|
else
|
|
125
172
|
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
|
126
173
|
end
|
|
127
174
|
end
|
|
128
175
|
|
|
176
|
+
attr_reader :top_level
|
|
177
|
+
|
|
178
|
+
# @api private
|
|
179
|
+
attr_reader :unfiltered_interface_type_memberships
|
|
180
|
+
|
|
181
|
+
def top_level_profile(refresh: false)
|
|
182
|
+
if refresh
|
|
183
|
+
@top_level_profile = nil
|
|
184
|
+
end
|
|
185
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
|
|
186
|
+
end
|
|
187
|
+
|
|
129
188
|
private
|
|
130
189
|
|
|
131
190
|
def ensure_all_loaded(types_to_visit)
|
|
@@ -137,6 +196,103 @@ module GraphQL
|
|
|
137
196
|
end
|
|
138
197
|
end
|
|
139
198
|
end
|
|
199
|
+
top_level_profile(refresh: true)
|
|
200
|
+
nil
|
|
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
|
|
140
296
|
end
|
|
141
297
|
end
|
|
142
298
|
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.
|
|
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.
|
|
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).
|
|
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).
|
|
407
|
+
if !possible_types(type_defn).empty?
|
|
395
408
|
true
|
|
396
409
|
else
|
|
397
410
|
if @context.respond_to?(:logger) && (logger = @context.logger)
|