graphql 2.4.3 → 2.5.2
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 +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +94 -51
- data/lib/graphql/execution/interpreter.rb +16 -7
- 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 +3 -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 +27 -13
- 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 +77 -40
- 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_deprecation_reason.rb +15 -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 +12 -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 +95 -243
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +93 -44
- 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 +283 -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 +62 -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
@@ -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,8 +148,8 @@ 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
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
|
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
|
-
@
|
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
|
-
|
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.
|
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)
|
data/lib/graphql/schema.rb
CHANGED
@@ -7,7 +7,6 @@ require "graphql/schema/find_inherited_value"
|
|
7
7
|
require "graphql/schema/finder"
|
8
8
|
require "graphql/schema/introspection_system"
|
9
9
|
require "graphql/schema/late_bound_type"
|
10
|
-
require "graphql/schema/null_mask"
|
11
10
|
require "graphql/schema/timeout"
|
12
11
|
require "graphql/schema/type_expression"
|
13
12
|
require "graphql/schema/unique_within_type"
|
@@ -73,6 +72,9 @@ module GraphQL
|
|
73
72
|
class Schema
|
74
73
|
extend GraphQL::Schema::Member::HasAstNode
|
75
74
|
extend GraphQL::Schema::FindInheritedValue
|
75
|
+
extend Autoload
|
76
|
+
|
77
|
+
autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
|
76
78
|
|
77
79
|
class DuplicateNamesError < GraphQL::Error
|
78
80
|
attr_reader :duplicated_name
|
@@ -109,7 +111,7 @@ module GraphQL
|
|
109
111
|
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
|
110
112
|
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
|
111
113
|
# @return [Class] the schema described by `document`
|
112
|
-
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
|
114
|
+
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}, base_types: {})
|
113
115
|
# If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
|
114
116
|
if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
|
115
117
|
GraphQL::Schema::BuildFromDefinition.from_definition_path(
|
@@ -118,6 +120,7 @@ module GraphQL
|
|
118
120
|
default_resolve: default_resolve,
|
119
121
|
parser: parser,
|
120
122
|
using: using,
|
123
|
+
base_types: base_types,
|
121
124
|
)
|
122
125
|
else
|
123
126
|
GraphQL::Schema::BuildFromDefinition.from_definition(
|
@@ -126,6 +129,7 @@ module GraphQL
|
|
126
129
|
default_resolve: default_resolve,
|
127
130
|
parser: parser,
|
128
131
|
using: using,
|
132
|
+
base_types: base_types,
|
129
133
|
)
|
130
134
|
end
|
131
135
|
end
|
@@ -164,9 +168,6 @@ module GraphQL
|
|
164
168
|
mods.each { |mod| new_class.include(mod) }
|
165
169
|
new_class.include(DefaultTraceClass)
|
166
170
|
trace_mode(:default, new_class)
|
167
|
-
backtrace_class = Class.new(new_class)
|
168
|
-
backtrace_class.include(GraphQL::Backtrace::Trace)
|
169
|
-
trace_mode(:default_backtrace, backtrace_class)
|
170
171
|
end
|
171
172
|
trace_class_for(:default, build: true)
|
172
173
|
end
|
@@ -213,11 +214,6 @@ module GraphQL
|
|
213
214
|
const_set(:DefaultTrace, Class.new(base_class) do
|
214
215
|
include DefaultTraceClass
|
215
216
|
end)
|
216
|
-
when :default_backtrace
|
217
|
-
schema_base_class = trace_class_for(:default, build: true)
|
218
|
-
const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
|
219
|
-
include(GraphQL::Backtrace::Trace)
|
220
|
-
end)
|
221
217
|
else
|
222
218
|
# First, see if the superclass has a custom-defined class for this.
|
223
219
|
# Then, if it doesn't, use this class's default trace
|
@@ -233,7 +229,7 @@ module GraphQL
|
|
233
229
|
add_trace_options_for(mode, default_options)
|
234
230
|
|
235
231
|
Class.new(base_class) do
|
236
|
-
mods.
|
232
|
+
!mods.empty? && include(*mods)
|
237
233
|
end
|
238
234
|
end
|
239
235
|
end
|
@@ -321,7 +317,7 @@ module GraphQL
|
|
321
317
|
# @param plugin [#use] A Schema plugin
|
322
318
|
# @return void
|
323
319
|
def use(plugin, **kwargs)
|
324
|
-
if kwargs.
|
320
|
+
if !kwargs.empty?
|
325
321
|
plugin.use(self, **kwargs)
|
326
322
|
else
|
327
323
|
plugin.use(self)
|
@@ -446,7 +442,12 @@ module GraphQL
|
|
446
442
|
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
447
443
|
elsif use_visibility_profile?
|
448
444
|
if block_given?
|
449
|
-
|
445
|
+
if visibility.preload?
|
446
|
+
@query_object = lazy_load_block.call
|
447
|
+
self.visibility.query_configured(@query_object)
|
448
|
+
else
|
449
|
+
@query_object = lazy_load_block
|
450
|
+
end
|
450
451
|
else
|
451
452
|
@query_object = new_query_object
|
452
453
|
self.visibility.query_configured(@query_object)
|
@@ -480,7 +481,12 @@ module GraphQL
|
|
480
481
|
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
481
482
|
elsif use_visibility_profile?
|
482
483
|
if block_given?
|
483
|
-
|
484
|
+
if visibility.preload?
|
485
|
+
@mutation_object = lazy_load_block.call
|
486
|
+
self.visibility.mutation_configured(@mutation_object)
|
487
|
+
else
|
488
|
+
@mutation_object = lazy_load_block
|
489
|
+
end
|
484
490
|
else
|
485
491
|
@mutation_object = new_mutation_object
|
486
492
|
self.visibility.mutation_configured(@mutation_object)
|
@@ -514,7 +520,12 @@ module GraphQL
|
|
514
520
|
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
515
521
|
elsif use_visibility_profile?
|
516
522
|
if block_given?
|
517
|
-
|
523
|
+
if visibility.preload?
|
524
|
+
@subscription_object = lazy_load_block.call
|
525
|
+
visibility.subscription_configured(@subscription_object)
|
526
|
+
else
|
527
|
+
@subscription_object = lazy_load_block
|
528
|
+
end
|
518
529
|
else
|
519
530
|
@subscription_object = new_subscription_object
|
520
531
|
self.visibility.subscription_configured(@subscription_object)
|
@@ -676,7 +687,7 @@ module GraphQL
|
|
676
687
|
# and generally speaking, we won't inherit any values.
|
677
688
|
# So optimize the most common case -- don't create a duplicate Hash.
|
678
689
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
679
|
-
if inherited_value.
|
690
|
+
if !inherited_value.empty?
|
680
691
|
inherited_value.merge(own_references_to)
|
681
692
|
else
|
682
693
|
own_references_to
|
@@ -812,13 +823,13 @@ module GraphQL
|
|
812
823
|
|
813
824
|
attr_writer :validate_timeout
|
814
825
|
|
815
|
-
def validate_timeout(new_validate_timeout =
|
816
|
-
if new_validate_timeout
|
826
|
+
def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
|
827
|
+
if !NOT_CONFIGURED.equal?(new_validate_timeout)
|
817
828
|
@validate_timeout = new_validate_timeout
|
818
829
|
elsif defined?(@validate_timeout)
|
819
830
|
@validate_timeout
|
820
831
|
else
|
821
|
-
find_inherited_value(:validate_timeout)
|
832
|
+
find_inherited_value(:validate_timeout) || 3
|
822
833
|
end
|
823
834
|
end
|
824
835
|
|
@@ -962,7 +973,7 @@ module GraphQL
|
|
962
973
|
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
963
974
|
# @return [Array<Module>] Type definitions added to this schema
|
964
975
|
def extra_types(*new_extra_types)
|
965
|
-
if new_extra_types.
|
976
|
+
if !new_extra_types.empty?
|
966
977
|
new_extra_types = new_extra_types.flatten
|
967
978
|
@own_extra_types ||= []
|
968
979
|
@own_extra_types.concat(new_extra_types)
|
@@ -987,10 +998,10 @@ module GraphQL
|
|
987
998
|
# @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
|
988
999
|
# @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
|
989
1000
|
def orphan_types(*new_orphan_types)
|
990
|
-
if new_orphan_types.
|
1001
|
+
if !new_orphan_types.empty?
|
991
1002
|
new_orphan_types = new_orphan_types.flatten
|
992
1003
|
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
993
|
-
if non_object_types.
|
1004
|
+
if !non_object_types.empty?
|
994
1005
|
raise ArgumentError, <<~ERR
|
995
1006
|
Only object type classes should be added as `orphan_types(...)`.
|
996
1007
|
|
@@ -1007,7 +1018,7 @@ module GraphQL
|
|
1007
1018
|
|
1008
1019
|
inherited_ot = find_inherited_value(:orphan_types, nil)
|
1009
1020
|
if inherited_ot
|
1010
|
-
if own_orphan_types.
|
1021
|
+
if !own_orphan_types.empty?
|
1011
1022
|
inherited_ot + own_orphan_types
|
1012
1023
|
else
|
1013
1024
|
inherited_ot
|
@@ -1100,6 +1111,9 @@ module GraphQL
|
|
1100
1111
|
}
|
1101
1112
|
end
|
1102
1113
|
|
1114
|
+
# @api private
|
1115
|
+
attr_accessor :using_backtrace
|
1116
|
+
|
1103
1117
|
# @api private
|
1104
1118
|
def handle_or_reraise(context, err)
|
1105
1119
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
@@ -1113,6 +1127,10 @@ module GraphQL
|
|
1113
1127
|
end
|
1114
1128
|
handler[:handler].call(err, obj, args, context, field)
|
1115
1129
|
else
|
1130
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
1131
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
1132
|
+
end
|
1133
|
+
|
1116
1134
|
raise err
|
1117
1135
|
end
|
1118
1136
|
end
|
@@ -1282,7 +1300,10 @@ module GraphQL
|
|
1282
1300
|
def type_error(type_error, ctx)
|
1283
1301
|
case type_error
|
1284
1302
|
when GraphQL::InvalidNullError
|
1285
|
-
|
1303
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_node: type_error.ast_node)
|
1304
|
+
execution_error.path = ctx[:current_path]
|
1305
|
+
|
1306
|
+
ctx.errors << execution_error
|
1286
1307
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
1287
1308
|
raise type_error
|
1288
1309
|
when GraphQL::IntegerDecodingError
|
@@ -1317,12 +1338,12 @@ module GraphQL
|
|
1317
1338
|
# Add several directives at once
|
1318
1339
|
# @param new_directives [Class]
|
1319
1340
|
def directives(*new_directives)
|
1320
|
-
if new_directives.
|
1341
|
+
if !new_directives.empty?
|
1321
1342
|
new_directives.flatten.each { |d| directive(d) }
|
1322
1343
|
end
|
1323
1344
|
|
1324
1345
|
inherited_dirs = find_inherited_value(:directives, default_directives)
|
1325
|
-
if own_directives.
|
1346
|
+
if !own_directives.empty?
|
1326
1347
|
inherited_dirs.merge(own_directives)
|
1327
1348
|
else
|
1328
1349
|
inherited_dirs
|
@@ -1350,6 +1371,16 @@ module GraphQL
|
|
1350
1371
|
}.freeze
|
1351
1372
|
end
|
1352
1373
|
|
1374
|
+
# @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema
|
1375
|
+
attr_accessor :detailed_trace
|
1376
|
+
|
1377
|
+
# @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex})
|
1378
|
+
# @return [Boolean] When `true`, save a detailed trace for this query.
|
1379
|
+
# @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true
|
1380
|
+
def detailed_trace?(query)
|
1381
|
+
raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition."
|
1382
|
+
end
|
1383
|
+
|
1353
1384
|
def tracer(new_tracer, silence_deprecation_warning: false)
|
1354
1385
|
if !silence_deprecation_warning
|
1355
1386
|
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
@@ -1367,14 +1398,22 @@ module GraphQL
|
|
1367
1398
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
1368
1399
|
end
|
1369
1400
|
|
1370
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
1371
|
-
#
|
1401
|
+
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
1402
|
+
#
|
1403
|
+
# You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`,
|
1404
|
+
# it will only run for queries with a matching `context[:trace_mode]`.
|
1405
|
+
#
|
1406
|
+
# Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration).
|
1407
|
+
#
|
1408
|
+
# @example Adding a trace in a special mode
|
1409
|
+
# # only runs when `query.context[:trace_mode]` is `:special`
|
1410
|
+
# trace_with SpecialTrace, mode: :special
|
1372
1411
|
#
|
1373
1412
|
# @param trace_mod [Module] A module that implements tracing methods
|
1374
1413
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
1375
1414
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1376
1415
|
# @return [void]
|
1377
|
-
# @see GraphQL::Tracing::Trace for available tracing methods
|
1416
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
1378
1417
|
def trace_with(trace_mod, mode: :default, **options)
|
1379
1418
|
if mode.is_a?(Array)
|
1380
1419
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
@@ -1424,29 +1463,36 @@ module GraphQL
|
|
1424
1463
|
#
|
1425
1464
|
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1426
1465
|
#
|
1466
|
+
# If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then
|
1467
|
+
# DetailedTrace's mode will override the passed-in `mode`.
|
1468
|
+
#
|
1427
1469
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1428
1470
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1429
1471
|
# @return [Tracing::Trace]
|
1430
1472
|
def new_trace(mode: nil, **options)
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1441
|
-
options_trace_mode = :default
|
1442
|
-
:default_backtrace
|
1473
|
+
should_sample = if detailed_trace
|
1474
|
+
if (query = options[:query])
|
1475
|
+
detailed_trace?(query)
|
1476
|
+
elsif (multiplex = options[:multiplex])
|
1477
|
+
if multiplex.queries.length == 1
|
1478
|
+
detailed_trace?(multiplex.queries.first)
|
1479
|
+
else
|
1480
|
+
detailed_trace?(multiplex)
|
1481
|
+
end
|
1443
1482
|
end
|
1444
1483
|
else
|
1445
|
-
|
1484
|
+
false
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
if should_sample
|
1488
|
+
mode = detailed_trace.trace_mode
|
1489
|
+
else
|
1490
|
+
target = options[:query] || options[:multiplex]
|
1491
|
+
mode ||= target && target.context[:trace_mode]
|
1446
1492
|
end
|
1447
1493
|
|
1448
|
-
|
1449
|
-
base_trace_options = trace_options_for(
|
1494
|
+
trace_mode = mode || default_trace_mode
|
1495
|
+
base_trace_options = trace_options_for(trace_mode)
|
1450
1496
|
trace_options = base_trace_options.merge(options)
|
1451
1497
|
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1452
1498
|
trace_class_for_mode.new(**trace_options)
|
@@ -1787,3 +1833,6 @@ module GraphQL
|
|
1787
1833
|
end
|
1788
1834
|
end
|
1789
1835
|
end
|
1836
|
+
|
1837
|
+
require "graphql/schema/loader"
|
1838
|
+
require "graphql/schema/printer"
|
@@ -34,7 +34,7 @@ module GraphQL
|
|
34
34
|
GraphQL::StaticValidation::VariableUsagesAreAllowed,
|
35
35
|
GraphQL::StaticValidation::MutationRootExists,
|
36
36
|
GraphQL::StaticValidation::QueryRootExists,
|
37
|
-
GraphQL::StaticValidation::
|
37
|
+
GraphQL::StaticValidation::SubscriptionRootExistsAndSingleSubscriptionSelection,
|
38
38
|
GraphQL::StaticValidation::InputObjectNamesAreUnique,
|
39
39
|
GraphQL::StaticValidation::OneOfInputObjectsAreValid,
|
40
40
|
]
|
@@ -16,7 +16,7 @@ module GraphQL
|
|
16
16
|
|
17
17
|
def validate_arguments(node)
|
18
18
|
argument_defns = node.arguments
|
19
|
-
if argument_defns.
|
19
|
+
if !argument_defns.empty?
|
20
20
|
args_by_name = Hash.new { |h, k| h[k] = [] }
|
21
21
|
argument_defns.each { |a| args_by_name[a.name] << a }
|
22
22
|
args_by_name.each do |name, defns|
|