graphql 2.4.3 → 2.4.13
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 +95 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- 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/dashboard.css +3 -0
- data/lib/graphql/dashboard/statics/dashboard.js +78 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +63 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +60 -0
- data/lib/graphql/dashboard.rb +142 -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 +67 -40
- data/lib/graphql/execution/interpreter.rb +16 -6
- data/lib/graphql/execution/multiplex.rb +0 -4
- 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 +2 -2
- 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 +20 -22
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +8 -7
- 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 +70 -34
- 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 +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +90 -43
- 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/required_arguments_are_present.rb +1 -1
- 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 +2 -2
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -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 +2 -0
- data/lib/graphql/tracing/appsignal_trace.rb +12 -0
- 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 +11 -0
- 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/new_relic_trace.rb +164 -41
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +4 -0
- 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 +737 -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 +31 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +11 -0
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +11 -0
- data/lib/graphql/tracing/statsd_trace.rb +15 -0
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +128 -1
- data/lib/graphql/tracing.rb +30 -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 +152 -10
- 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
@@ -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
|
@@ -164,9 +166,6 @@ module GraphQL
|
|
164
166
|
mods.each { |mod| new_class.include(mod) }
|
165
167
|
new_class.include(DefaultTraceClass)
|
166
168
|
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
169
|
end
|
171
170
|
trace_class_for(:default, build: true)
|
172
171
|
end
|
@@ -213,11 +212,6 @@ module GraphQL
|
|
213
212
|
const_set(:DefaultTrace, Class.new(base_class) do
|
214
213
|
include DefaultTraceClass
|
215
214
|
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
215
|
else
|
222
216
|
# First, see if the superclass has a custom-defined class for this.
|
223
217
|
# Then, if it doesn't, use this class's default trace
|
@@ -233,7 +227,7 @@ module GraphQL
|
|
233
227
|
add_trace_options_for(mode, default_options)
|
234
228
|
|
235
229
|
Class.new(base_class) do
|
236
|
-
mods.
|
230
|
+
!mods.empty? && include(*mods)
|
237
231
|
end
|
238
232
|
end
|
239
233
|
end
|
@@ -321,7 +315,7 @@ module GraphQL
|
|
321
315
|
# @param plugin [#use] A Schema plugin
|
322
316
|
# @return void
|
323
317
|
def use(plugin, **kwargs)
|
324
|
-
if kwargs.
|
318
|
+
if !kwargs.empty?
|
325
319
|
plugin.use(self, **kwargs)
|
326
320
|
else
|
327
321
|
plugin.use(self)
|
@@ -446,7 +440,12 @@ module GraphQL
|
|
446
440
|
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
447
441
|
elsif use_visibility_profile?
|
448
442
|
if block_given?
|
449
|
-
|
443
|
+
if visibility.preload?
|
444
|
+
@query_object = lazy_load_block.call
|
445
|
+
self.visibility.query_configured(@query_object)
|
446
|
+
else
|
447
|
+
@query_object = lazy_load_block
|
448
|
+
end
|
450
449
|
else
|
451
450
|
@query_object = new_query_object
|
452
451
|
self.visibility.query_configured(@query_object)
|
@@ -480,7 +479,12 @@ module GraphQL
|
|
480
479
|
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
481
480
|
elsif use_visibility_profile?
|
482
481
|
if block_given?
|
483
|
-
|
482
|
+
if visibility.preload?
|
483
|
+
@mutation_object = lazy_load_block.call
|
484
|
+
self.visibility.mutation_configured(@mutation_object)
|
485
|
+
else
|
486
|
+
@mutation_object = lazy_load_block
|
487
|
+
end
|
484
488
|
else
|
485
489
|
@mutation_object = new_mutation_object
|
486
490
|
self.visibility.mutation_configured(@mutation_object)
|
@@ -514,7 +518,12 @@ module GraphQL
|
|
514
518
|
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
515
519
|
elsif use_visibility_profile?
|
516
520
|
if block_given?
|
517
|
-
|
521
|
+
if visibility.preload?
|
522
|
+
@subscription_object = lazy_load_block.call
|
523
|
+
visibility.subscription_configured(@subscription_object)
|
524
|
+
else
|
525
|
+
@subscription_object = lazy_load_block
|
526
|
+
end
|
518
527
|
else
|
519
528
|
@subscription_object = new_subscription_object
|
520
529
|
self.visibility.subscription_configured(@subscription_object)
|
@@ -676,7 +685,7 @@ module GraphQL
|
|
676
685
|
# and generally speaking, we won't inherit any values.
|
677
686
|
# So optimize the most common case -- don't create a duplicate Hash.
|
678
687
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
679
|
-
if inherited_value.
|
688
|
+
if !inherited_value.empty?
|
680
689
|
inherited_value.merge(own_references_to)
|
681
690
|
else
|
682
691
|
own_references_to
|
@@ -812,13 +821,13 @@ module GraphQL
|
|
812
821
|
|
813
822
|
attr_writer :validate_timeout
|
814
823
|
|
815
|
-
def validate_timeout(new_validate_timeout =
|
816
|
-
if new_validate_timeout
|
824
|
+
def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
|
825
|
+
if !NOT_CONFIGURED.equal?(new_validate_timeout)
|
817
826
|
@validate_timeout = new_validate_timeout
|
818
827
|
elsif defined?(@validate_timeout)
|
819
828
|
@validate_timeout
|
820
829
|
else
|
821
|
-
find_inherited_value(:validate_timeout)
|
830
|
+
find_inherited_value(:validate_timeout) || 3
|
822
831
|
end
|
823
832
|
end
|
824
833
|
|
@@ -962,7 +971,7 @@ module GraphQL
|
|
962
971
|
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
963
972
|
# @return [Array<Module>] Type definitions added to this schema
|
964
973
|
def extra_types(*new_extra_types)
|
965
|
-
if new_extra_types.
|
974
|
+
if !new_extra_types.empty?
|
966
975
|
new_extra_types = new_extra_types.flatten
|
967
976
|
@own_extra_types ||= []
|
968
977
|
@own_extra_types.concat(new_extra_types)
|
@@ -987,10 +996,10 @@ module GraphQL
|
|
987
996
|
# @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
|
988
997
|
# @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
|
989
998
|
def orphan_types(*new_orphan_types)
|
990
|
-
if new_orphan_types.
|
999
|
+
if !new_orphan_types.empty?
|
991
1000
|
new_orphan_types = new_orphan_types.flatten
|
992
1001
|
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
993
|
-
if non_object_types.
|
1002
|
+
if !non_object_types.empty?
|
994
1003
|
raise ArgumentError, <<~ERR
|
995
1004
|
Only object type classes should be added as `orphan_types(...)`.
|
996
1005
|
|
@@ -1007,7 +1016,7 @@ module GraphQL
|
|
1007
1016
|
|
1008
1017
|
inherited_ot = find_inherited_value(:orphan_types, nil)
|
1009
1018
|
if inherited_ot
|
1010
|
-
if own_orphan_types.
|
1019
|
+
if !own_orphan_types.empty?
|
1011
1020
|
inherited_ot + own_orphan_types
|
1012
1021
|
else
|
1013
1022
|
inherited_ot
|
@@ -1100,6 +1109,9 @@ module GraphQL
|
|
1100
1109
|
}
|
1101
1110
|
end
|
1102
1111
|
|
1112
|
+
# @api private
|
1113
|
+
attr_accessor :using_backtrace
|
1114
|
+
|
1103
1115
|
# @api private
|
1104
1116
|
def handle_or_reraise(context, err)
|
1105
1117
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
@@ -1113,6 +1125,10 @@ module GraphQL
|
|
1113
1125
|
end
|
1114
1126
|
handler[:handler].call(err, obj, args, context, field)
|
1115
1127
|
else
|
1128
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
1129
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
1130
|
+
end
|
1131
|
+
|
1116
1132
|
raise err
|
1117
1133
|
end
|
1118
1134
|
end
|
@@ -1282,7 +1298,10 @@ module GraphQL
|
|
1282
1298
|
def type_error(type_error, ctx)
|
1283
1299
|
case type_error
|
1284
1300
|
when GraphQL::InvalidNullError
|
1285
|
-
|
1301
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_node: type_error.ast_node)
|
1302
|
+
execution_error.path = ctx[:current_path]
|
1303
|
+
|
1304
|
+
ctx.errors << execution_error
|
1286
1305
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
1287
1306
|
raise type_error
|
1288
1307
|
when GraphQL::IntegerDecodingError
|
@@ -1317,12 +1336,12 @@ module GraphQL
|
|
1317
1336
|
# Add several directives at once
|
1318
1337
|
# @param new_directives [Class]
|
1319
1338
|
def directives(*new_directives)
|
1320
|
-
if new_directives.
|
1339
|
+
if !new_directives.empty?
|
1321
1340
|
new_directives.flatten.each { |d| directive(d) }
|
1322
1341
|
end
|
1323
1342
|
|
1324
1343
|
inherited_dirs = find_inherited_value(:directives, default_directives)
|
1325
|
-
if own_directives.
|
1344
|
+
if !own_directives.empty?
|
1326
1345
|
inherited_dirs.merge(own_directives)
|
1327
1346
|
else
|
1328
1347
|
inherited_dirs
|
@@ -1350,6 +1369,16 @@ module GraphQL
|
|
1350
1369
|
}.freeze
|
1351
1370
|
end
|
1352
1371
|
|
1372
|
+
# @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema
|
1373
|
+
attr_accessor :detailed_trace
|
1374
|
+
|
1375
|
+
# @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex})
|
1376
|
+
# @return [Boolean] When `true`, save a detailed trace for this query.
|
1377
|
+
# @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true
|
1378
|
+
def detailed_trace?(query)
|
1379
|
+
raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition."
|
1380
|
+
end
|
1381
|
+
|
1353
1382
|
def tracer(new_tracer, silence_deprecation_warning: false)
|
1354
1383
|
if !silence_deprecation_warning
|
1355
1384
|
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
@@ -1367,14 +1396,22 @@ module GraphQL
|
|
1367
1396
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
1368
1397
|
end
|
1369
1398
|
|
1370
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
1371
|
-
#
|
1399
|
+
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
1400
|
+
#
|
1401
|
+
# You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`,
|
1402
|
+
# it will only run for queries with a matching `context[:trace_mode]`.
|
1403
|
+
#
|
1404
|
+
# Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration).
|
1405
|
+
#
|
1406
|
+
# @example Adding a trace in a special mode
|
1407
|
+
# # only runs when `query.context[:trace_mode]` is `:special`
|
1408
|
+
# trace_with SpecialTrace, mode: :special
|
1372
1409
|
#
|
1373
1410
|
# @param trace_mod [Module] A module that implements tracing methods
|
1374
1411
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
1375
1412
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1376
1413
|
# @return [void]
|
1377
|
-
# @see GraphQL::Tracing::Trace for available tracing methods
|
1414
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
1378
1415
|
def trace_with(trace_mod, mode: :default, **options)
|
1379
1416
|
if mode.is_a?(Array)
|
1380
1417
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
@@ -1424,29 +1461,36 @@ module GraphQL
|
|
1424
1461
|
#
|
1425
1462
|
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1426
1463
|
#
|
1464
|
+
# If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then
|
1465
|
+
# DetailedTrace's mode will override the passed-in `mode`.
|
1466
|
+
#
|
1427
1467
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1428
1468
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1429
1469
|
# @return [Tracing::Trace]
|
1430
1470
|
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
|
1471
|
+
should_sample = if detailed_trace
|
1472
|
+
if (query = options[:query])
|
1473
|
+
detailed_trace?(query)
|
1474
|
+
elsif (multiplex = options[:multiplex])
|
1475
|
+
if multiplex.queries.length == 1
|
1476
|
+
detailed_trace?(multiplex.queries.first)
|
1477
|
+
else
|
1478
|
+
detailed_trace?(multiplex)
|
1479
|
+
end
|
1443
1480
|
end
|
1444
1481
|
else
|
1445
|
-
|
1482
|
+
false
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
if should_sample
|
1486
|
+
mode = detailed_trace.trace_mode
|
1487
|
+
else
|
1488
|
+
target = options[:query] || options[:multiplex]
|
1489
|
+
mode ||= target && target.context[:trace_mode]
|
1446
1490
|
end
|
1447
1491
|
|
1448
|
-
|
1449
|
-
base_trace_options = trace_options_for(
|
1492
|
+
trace_mode = mode || default_trace_mode
|
1493
|
+
base_trace_options = trace_options_for(trace_mode)
|
1450
1494
|
trace_options = base_trace_options.merge(options)
|
1451
1495
|
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1452
1496
|
trace_class_for_mode.new(**trace_options)
|
@@ -1787,3 +1831,6 @@ module GraphQL
|
|
1787
1831
|
end
|
1788
1832
|
end
|
1789
1833
|
end
|
1834
|
+
|
1835
|
+
require "graphql/schema/loader"
|
1836
|
+
require "graphql/schema/printer"
|
@@ -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|
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
def validate_field_selections(ast_node, resolved_type)
|
26
26
|
msg = if resolved_type.nil?
|
27
27
|
nil
|
28
|
-
elsif ast_node.selections.
|
28
|
+
elsif !ast_node.selections.empty? && resolved_type.kind.leaf?
|
29
29
|
selection_strs = ast_node.selections.map do |n|
|
30
30
|
case n
|
31
31
|
when GraphQL::Language::Nodes::InlineFragment
|
@@ -345,7 +345,7 @@ module GraphQL
|
|
345
345
|
fields << Field.new(node, definition, owner_type, parents)
|
346
346
|
when GraphQL::Language::Nodes::InlineFragment
|
347
347
|
fragment_type = node.type ? @types.type(node.type.name) : owner_type
|
348
|
-
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type:
|
348
|
+
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: fragment_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
|
349
349
|
when GraphQL::Language::Nodes::FragmentSpread
|
350
350
|
fragment_spreads << FragmentSpread.new(node.name, parents)
|
351
351
|
end
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
|
33
33
|
def on_document(node, parent)
|
34
34
|
super
|
35
|
-
if
|
35
|
+
if !@schema_definition_nodes.empty?
|
36
36
|
add_error(GraphQL::StaticValidation::NoDefinitionsArePresentError.new(%|Query cannot contain schema definitions|, nodes: @schema_definition_nodes))
|
37
37
|
end
|
38
38
|
end
|