graphql 2.4.8 → 2.4.11
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/graphql/backtrace/table.rb +95 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +5 -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/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 +17 -5
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/dataloader.rb +37 -5
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +60 -33
- data/lib/graphql/execution/interpreter.rb +9 -1
- 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 +6 -12
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/query.rb +8 -12
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +17 -8
- data/lib/graphql/schema/resolver.rb +2 -5
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/visibility/profile.rb +5 -5
- data/lib/graphql/schema/visibility.rb +14 -9
- data/lib/graphql/schema.rb +54 -28
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -3
- data/lib/graphql/tracing/active_support_notifications_trace.rb +6 -2
- data/lib/graphql/tracing/appoptics_trace.rb +3 -1
- data/lib/graphql/tracing/appsignal_trace.rb +6 -0
- data/lib/graphql/tracing/data_dog_trace.rb +5 -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/new_relic_trace.rb +147 -41
- 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/prometheus_trace.rb +22 -0
- data/lib/graphql/tracing/scout_trace.rb +6 -0
- data/lib/graphql/tracing/sentry_trace.rb +5 -0
- data/lib/graphql/tracing/statsd_trace.rb +9 -0
- data/lib/graphql/tracing/trace.rb +125 -1
- data/lib/graphql/tracing.rb +2 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +148 -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
@@ -2,7 +2,7 @@
|
|
2
2
|
module GraphQL
|
3
3
|
# Raised automatically when a field's resolve function returns `nil`
|
4
4
|
# for a non-null field.
|
5
|
-
class InvalidNullError < GraphQL::
|
5
|
+
class InvalidNullError < GraphQL::Error
|
6
6
|
# @return [GraphQL::BaseType] The owner of {#field}
|
7
7
|
attr_reader :parent_type
|
8
8
|
|
@@ -12,23 +12,17 @@ module GraphQL
|
|
12
12
|
# @return [nil, GraphQL::ExecutionError] The invalid value for this field
|
13
13
|
attr_reader :value
|
14
14
|
|
15
|
-
|
15
|
+
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
|
16
|
+
attr_reader :ast_node
|
17
|
+
|
18
|
+
def initialize(parent_type, field, value, ast_node)
|
16
19
|
@parent_type = parent_type
|
17
20
|
@field = field
|
18
21
|
@value = value
|
22
|
+
@ast_node = ast_node
|
19
23
|
super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
|
20
24
|
end
|
21
25
|
|
22
|
-
# @return [Hash] An entry for the response's "errors" key
|
23
|
-
def to_h
|
24
|
-
{ "message" => message }
|
25
|
-
end
|
26
|
-
|
27
|
-
# @deprecated always false
|
28
|
-
def parent_error?
|
29
|
-
false
|
30
|
-
end
|
31
|
-
|
32
26
|
class << self
|
33
27
|
attr_accessor :parent_class
|
34
28
|
|
@@ -161,7 +161,7 @@ module GraphQL
|
|
161
161
|
expect_token(:VAR_SIGN)
|
162
162
|
var_name = parse_name
|
163
163
|
expect_token(:COLON)
|
164
|
-
var_type = self.type
|
164
|
+
var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
|
165
165
|
default_value = if at?(:EQUALS)
|
166
166
|
advance_token
|
167
167
|
value
|
data/lib/graphql/query.rb
CHANGED
@@ -97,21 +97,22 @@ module GraphQL
|
|
97
97
|
# @param root_value [Object] the object used to resolve fields on the root type
|
98
98
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
99
99
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
100
|
-
# @param visibility_profile [Symbol]
|
100
|
+
# @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]`
|
101
101
|
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
102
102
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
103
103
|
variables ||= {}
|
104
104
|
@schema = schema
|
105
105
|
@context = schema.context_class.new(query: self, values: context)
|
106
|
+
if visibility_profile
|
107
|
+
@context[:visibility_profile] ||= visibility_profile
|
108
|
+
end
|
106
109
|
|
107
110
|
if use_visibility_profile.nil?
|
108
111
|
use_visibility_profile = warden ? false : schema.use_visibility_profile?
|
109
112
|
end
|
110
113
|
|
111
|
-
@visibility_profile = visibility_profile
|
112
|
-
|
113
114
|
if use_visibility_profile
|
114
|
-
@visibility_profile = @schema.visibility.profile_for(@context
|
115
|
+
@visibility_profile = @schema.visibility.profile_for(@context)
|
115
116
|
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
|
116
117
|
else
|
117
118
|
@visibility_profile = nil
|
@@ -127,14 +128,6 @@ module GraphQL
|
|
127
128
|
context_tracers = (context ? context.fetch(:tracers, []) : [])
|
128
129
|
@tracers = schema.tracers + context_tracers
|
129
130
|
|
130
|
-
# Support `ctx[:backtrace] = true` for wrapping backtraces
|
131
|
-
if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
132
|
-
if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
|
133
|
-
context_tracers += [GraphQL::Backtrace::Tracer]
|
134
|
-
@tracers << GraphQL::Backtrace::Tracer
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
131
|
if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
|
139
132
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
140
133
|
end
|
@@ -448,6 +441,7 @@ module GraphQL
|
|
448
441
|
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
449
442
|
parse_error = nil
|
450
443
|
@document ||= begin
|
444
|
+
current_trace.begin_parse(query_string)
|
451
445
|
if query_string
|
452
446
|
GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
|
453
447
|
end
|
@@ -455,6 +449,8 @@ module GraphQL
|
|
455
449
|
parse_error = err
|
456
450
|
@schema.parse_error(err, @context)
|
457
451
|
nil
|
452
|
+
ensure
|
453
|
+
current_trace.end_parse(query_string)
|
458
454
|
end
|
459
455
|
|
460
456
|
@fragments = {}
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -61,12 +61,19 @@ module GraphQL
|
|
61
61
|
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
|
62
62
|
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
|
63
63
|
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
|
64
|
+
# @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
|
64
65
|
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
|
66
|
+
# @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation
|
65
67
|
# @return [void]
|
66
68
|
# @see {Schema::EnumValue} which handles these inputs by default
|
67
|
-
def value(*args, **kwargs, &block)
|
69
|
+
def value(*args, value_method: nil, **kwargs, &block)
|
68
70
|
kwargs[:owner] = self
|
69
71
|
value = enum_value_class.new(*args, **kwargs, &block)
|
72
|
+
|
73
|
+
if value_method || (value_methods && value_method != false)
|
74
|
+
generate_value_method(value, value_method)
|
75
|
+
end
|
76
|
+
|
70
77
|
key = value.graphql_name
|
71
78
|
prev_value = own_values[key]
|
72
79
|
case prev_value
|
@@ -154,6 +161,18 @@ module GraphQL
|
|
154
161
|
end
|
155
162
|
end
|
156
163
|
|
164
|
+
def value_methods(new_value = NOT_CONFIGURED)
|
165
|
+
if NOT_CONFIGURED.equal?(new_value)
|
166
|
+
if @value_methods != nil
|
167
|
+
@value_methods
|
168
|
+
else
|
169
|
+
find_inherited_value(:value_methods, false)
|
170
|
+
end
|
171
|
+
else
|
172
|
+
@value_methods = new_value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
157
176
|
def kind
|
158
177
|
GraphQL::TypeKinds::ENUM
|
159
178
|
end
|
@@ -215,6 +234,7 @@ module GraphQL
|
|
215
234
|
# because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
|
216
235
|
child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
|
217
236
|
end
|
237
|
+
child_class.class_eval { @value_methods = nil }
|
218
238
|
super
|
219
239
|
end
|
220
240
|
|
@@ -223,6 +243,21 @@ module GraphQL
|
|
223
243
|
def own_values
|
224
244
|
@own_values ||= {}
|
225
245
|
end
|
246
|
+
|
247
|
+
def generate_value_method(value, configured_value_method)
|
248
|
+
return if configured_value_method == false
|
249
|
+
|
250
|
+
value_method_name = configured_value_method || value.graphql_name.downcase
|
251
|
+
|
252
|
+
if respond_to?(value_method_name.to_sym)
|
253
|
+
warn "Failed to define value method for :#{value_method_name}, because " \
|
254
|
+
"#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \
|
255
|
+
"or `value_method: false` to disable Enum value method generation."
|
256
|
+
return
|
257
|
+
end
|
258
|
+
|
259
|
+
instance_eval("def #{value_method_name}; #{value.graphql_name.inspect}; end;", __FILE__, __LINE__)
|
260
|
+
end
|
226
261
|
end
|
227
262
|
|
228
263
|
enum_value_class(GraphQL::Schema::EnumValue)
|
@@ -13,6 +13,7 @@ module GraphQL
|
|
13
13
|
include GraphQL::Schema::Member::Scoped
|
14
14
|
include GraphQL::Schema::Member::HasAstNode
|
15
15
|
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
16
|
+
include GraphQL::Schema::Member::HasDataloader
|
16
17
|
include GraphQL::Schema::Member::HasDirectives
|
17
18
|
include GraphQL::Schema::Member::HasInterfaces
|
18
19
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
module HasDataloader
|
7
|
+
# @return [GraphQL::Dataloader] The dataloader for the currently-running query
|
8
|
+
def dataloader
|
9
|
+
context.dataloader
|
10
|
+
end
|
11
|
+
|
12
|
+
# A shortcut method for loading a key from a source.
|
13
|
+
# Identical to `dataloader.with(source_class, *source_args).load(load_key)`
|
14
|
+
# @param source_class [Class<GraphQL::Dataloader::Source>]
|
15
|
+
# @param source_args [Array<Object>] Any extra parameters defined in `source_class`'s `initialize` method
|
16
|
+
# @param load_key [Object] The key to look up using `def fetch`
|
17
|
+
def dataload(source_class, *source_args, load_key)
|
18
|
+
dataloader.with(source_class, *source_args).load(load_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find an object with ActiveRecord via {Dataloader::ActiveRecordSource}.
|
22
|
+
# @param model [Class<ActiveRecord::Base>]
|
23
|
+
# @param find_by_value [Object] Usually an `id`, might be another value if `find_by:` is also provided
|
24
|
+
# @param find_by [Symbol, String] A column name to look the record up by. (Defaults to the model's primary key.)
|
25
|
+
# @return [ActiveRecord::Base, nil]
|
26
|
+
# @example Finding a record by ID
|
27
|
+
# dataload_record(Post, 5) # Like `Post.find(5)`, but dataloaded
|
28
|
+
# @example Finding a record by another attribute
|
29
|
+
# dataload_record(User, "matz", find_by: :handle) # Like `User.find_by(handle: "matz")`, but dataloaded
|
30
|
+
def dataload_record(model, find_by_value, find_by: nil)
|
31
|
+
source = if find_by
|
32
|
+
dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
|
33
|
+
else
|
34
|
+
dataloader.with(Dataloader::ActiveRecordSource, model)
|
35
|
+
end
|
36
|
+
|
37
|
+
source.load(find_by_value)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Look up an associated record using a Rails association.
|
41
|
+
# @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
|
42
|
+
# @param record [ActiveRecord::Base] The object that the association belongs to.
|
43
|
+
# @param scope [ActiveRecord::Relation] A scope to look up the associated record in
|
44
|
+
# @return [ActiveRecord::Base, nil] The associated record, if there is one
|
45
|
+
# @example Looking up a belongs_to on the current object
|
46
|
+
# dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded
|
47
|
+
# @example Looking up an associated record on some other object
|
48
|
+
# dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded
|
49
|
+
def dataload_association(record = object, association_name, scope: nil)
|
50
|
+
source = if scope
|
51
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
|
52
|
+
else
|
53
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
|
54
|
+
end
|
55
|
+
source.load(record)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'graphql/schema/member/base_dsl_methods'
|
3
3
|
require 'graphql/schema/member/graphql_type_names'
|
4
4
|
require 'graphql/schema/member/has_ast_node'
|
5
|
+
require 'graphql/schema/member/has_dataloader'
|
5
6
|
require 'graphql/schema/member/has_directives'
|
6
7
|
require 'graphql/schema/member/has_deprecation_reason'
|
7
8
|
require 'graphql/schema/member/has_interfaces'
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
7
7
|
class Object < GraphQL::Schema::Member
|
8
8
|
extend GraphQL::Schema::Member::HasFields
|
9
9
|
extend GraphQL::Schema::Member::HasInterfaces
|
10
|
+
include Member::HasDataloader
|
10
11
|
|
11
12
|
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
|
12
13
|
class FieldsAreRequiredError < GraphQL::Error
|
@@ -65,20 +66,28 @@ module GraphQL
|
|
65
66
|
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
|
66
67
|
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
|
67
68
|
def authorized_new(object, context)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
70
|
+
begin
|
71
|
+
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
|
72
|
+
begin
|
73
|
+
authorized?(object, context)
|
74
|
+
rescue GraphQL::UnauthorizedError => err
|
75
|
+
context.schema.unauthorized_object(err)
|
76
|
+
rescue StandardError => err
|
77
|
+
context.query.handle_or_reraise(err)
|
78
|
+
end
|
75
79
|
end
|
80
|
+
ensure
|
81
|
+
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
|
76
82
|
end
|
77
83
|
|
78
84
|
auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
|
79
85
|
GraphQL::Execution::Lazy.new do
|
86
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
80
87
|
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
|
81
|
-
context.schema.sync_lazy(maybe_lazy_auth_val)
|
88
|
+
res = context.schema.sync_lazy(maybe_lazy_auth_val)
|
89
|
+
context.query.current_trace.end_authorized(self, object, context, res)
|
90
|
+
res
|
82
91
|
end
|
83
92
|
end
|
84
93
|
else
|
@@ -22,11 +22,13 @@ module GraphQL
|
|
22
22
|
include Schema::Member::GraphQLTypeNames
|
23
23
|
# Really we only need description & comment from here, but:
|
24
24
|
extend Schema::Member::BaseDSLMethods
|
25
|
+
extend Member::BaseDSLMethods::ConfigurationExtension
|
25
26
|
extend GraphQL::Schema::Member::HasArguments
|
26
27
|
extend GraphQL::Schema::Member::HasValidators
|
27
28
|
include Schema::Member::HasPath
|
28
29
|
extend Schema::Member::HasPath
|
29
30
|
extend Schema::Member::HasDirectives
|
31
|
+
include Schema::Member::HasDataloader
|
30
32
|
|
31
33
|
# @param object [Object] The application object that this field is being resolved on
|
32
34
|
# @param context [GraphQL::Query::Context]
|
@@ -49,11 +51,6 @@ module GraphQL
|
|
49
51
|
# @return [GraphQL::Query::Context]
|
50
52
|
attr_reader :context
|
51
53
|
|
52
|
-
# @return [GraphQL::Dataloader]
|
53
|
-
def dataloader
|
54
|
-
context.dataloader
|
55
|
-
end
|
56
|
-
|
57
54
|
# @return [GraphQL::Schema::Field]
|
58
55
|
attr_reader :field
|
59
56
|
|
@@ -51,19 +51,36 @@ module GraphQL
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def validate(_object, context, value)
|
54
|
-
|
54
|
+
fully_matched_conditions = 0
|
55
|
+
partially_matched_conditions = 0
|
55
56
|
|
56
57
|
if !value.nil?
|
57
58
|
@one_of.each do |one_of_condition|
|
58
59
|
case one_of_condition
|
59
60
|
when Symbol
|
60
61
|
if value.key?(one_of_condition)
|
61
|
-
|
62
|
+
fully_matched_conditions += 1
|
62
63
|
end
|
63
64
|
when Array
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
any_match = false
|
66
|
+
full_match = true
|
67
|
+
|
68
|
+
one_of_condition.each do |k|
|
69
|
+
if value.key?(k)
|
70
|
+
any_match = true
|
71
|
+
else
|
72
|
+
full_match = false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
partial_match = !full_match && any_match
|
77
|
+
|
78
|
+
if full_match
|
79
|
+
fully_matched_conditions += 1
|
80
|
+
end
|
81
|
+
|
82
|
+
if partial_match
|
83
|
+
partially_matched_conditions += 1
|
67
84
|
end
|
68
85
|
else
|
69
86
|
raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
|
@@ -71,7 +88,7 @@ module GraphQL
|
|
71
88
|
end
|
72
89
|
end
|
73
90
|
|
74
|
-
if
|
91
|
+
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
75
92
|
nil # OK
|
76
93
|
else
|
77
94
|
@message || build_message(context)
|
@@ -18,7 +18,7 @@ module GraphQL
|
|
18
18
|
if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
|
19
19
|
types
|
20
20
|
else
|
21
|
-
schema.visibility.profile_for(ctx
|
21
|
+
schema.visibility.profile_for(ctx)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -159,7 +159,7 @@ module GraphQL
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
end
|
162
|
-
visible_f
|
162
|
+
visible_f&.ensure_loaded
|
163
163
|
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
164
164
|
f
|
165
165
|
else
|
@@ -319,9 +319,9 @@ module GraphQL
|
|
319
319
|
case type.kind.name
|
320
320
|
when "INTERFACE"
|
321
321
|
pts = []
|
322
|
-
@schema.visibility.all_interface_type_memberships[type].each do |itm|
|
323
|
-
if @cached_visible[itm] &&
|
324
|
-
pts <<
|
322
|
+
@schema.visibility.all_interface_type_memberships[type].each do |(itm, impl_type)|
|
323
|
+
if @cached_visible[itm] && @cached_visible[impl_type] && referenced?(impl_type)
|
324
|
+
pts << impl_type
|
325
325
|
end
|
326
326
|
end
|
327
327
|
pts
|
@@ -13,6 +13,10 @@ module GraphQL
|
|
13
13
|
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
|
14
14
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
15
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
|
+
}
|
16
20
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
17
21
|
if preload
|
18
22
|
schema.visibility.preload
|
@@ -81,8 +85,7 @@ module GraphQL
|
|
81
85
|
types_to_visit.compact!
|
82
86
|
ensure_all_loaded(types_to_visit)
|
83
87
|
@profiles.each do |profile_name, example_ctx|
|
84
|
-
|
85
|
-
prof = profile_for(example_ctx, profile_name)
|
88
|
+
prof = profile_for(example_ctx)
|
86
89
|
prof.all_types # force loading
|
87
90
|
end
|
88
91
|
end
|
@@ -145,7 +148,7 @@ module GraphQL
|
|
145
148
|
|
146
149
|
attr_reader :cached_profiles
|
147
150
|
|
148
|
-
def profile_for(context, visibility_profile)
|
151
|
+
def profile_for(context, visibility_profile = context[:visibility_profile])
|
149
152
|
if !@profiles.empty?
|
150
153
|
if visibility_profile.nil?
|
151
154
|
if @dynamic
|
@@ -160,7 +163,8 @@ module GraphQL
|
|
160
163
|
elsif !@profiles.include?(visibility_profile)
|
161
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."
|
162
165
|
else
|
163
|
-
@
|
166
|
+
profile_ctx = @profiles[visibility_profile]
|
167
|
+
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
|
164
168
|
end
|
165
169
|
elsif context.is_a?(Query::NullContext)
|
166
170
|
top_level_profile
|
@@ -222,7 +226,9 @@ module GraphQL
|
|
222
226
|
elsif member.respond_to?(:interface_type_memberships)
|
223
227
|
member.interface_type_memberships.each do |itm|
|
224
228
|
@all_references[itm.abstract_type] << member
|
225
|
-
|
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]
|
226
232
|
end
|
227
233
|
elsif member < GraphQL::Schema::Union
|
228
234
|
@unions_for_references << member
|
@@ -271,13 +277,12 @@ module GraphQL
|
|
271
277
|
|
272
278
|
# TODO: somehow don't iterate over all these,
|
273
279
|
# only the ones that may have been modified
|
274
|
-
@interface_type_memberships.each do |int_type,
|
280
|
+
@interface_type_memberships.each do |int_type, type_membership_pairs|
|
275
281
|
referers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
|
276
282
|
if !referers.empty?
|
277
|
-
|
278
|
-
implementor_type = type_membership.object_type
|
283
|
+
type_membership_pairs.each do |(type_membership, impl_type)|
|
279
284
|
# Add new items only:
|
280
|
-
@all_references[
|
285
|
+
@all_references[impl_type] |= referers
|
281
286
|
end
|
282
287
|
end
|
283
288
|
end
|
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"
|
@@ -167,9 +166,6 @@ module GraphQL
|
|
167
166
|
mods.each { |mod| new_class.include(mod) }
|
168
167
|
new_class.include(DefaultTraceClass)
|
169
168
|
trace_mode(:default, new_class)
|
170
|
-
backtrace_class = Class.new(new_class)
|
171
|
-
backtrace_class.include(GraphQL::Backtrace::Trace)
|
172
|
-
trace_mode(:default_backtrace, backtrace_class)
|
173
169
|
end
|
174
170
|
trace_class_for(:default, build: true)
|
175
171
|
end
|
@@ -216,11 +212,6 @@ module GraphQL
|
|
216
212
|
const_set(:DefaultTrace, Class.new(base_class) do
|
217
213
|
include DefaultTraceClass
|
218
214
|
end)
|
219
|
-
when :default_backtrace
|
220
|
-
schema_base_class = trace_class_for(:default, build: true)
|
221
|
-
const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
|
222
|
-
include(GraphQL::Backtrace::Trace)
|
223
|
-
end)
|
224
215
|
else
|
225
216
|
# First, see if the superclass has a custom-defined class for this.
|
226
217
|
# Then, if it doesn't, use this class's default trace
|
@@ -1118,6 +1109,9 @@ module GraphQL
|
|
1118
1109
|
}
|
1119
1110
|
end
|
1120
1111
|
|
1112
|
+
# @api private
|
1113
|
+
attr_accessor :using_backtrace
|
1114
|
+
|
1121
1115
|
# @api private
|
1122
1116
|
def handle_or_reraise(context, err)
|
1123
1117
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
@@ -1131,6 +1125,10 @@ module GraphQL
|
|
1131
1125
|
end
|
1132
1126
|
handler[:handler].call(err, obj, args, context, field)
|
1133
1127
|
else
|
1128
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
1129
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
1130
|
+
end
|
1131
|
+
|
1134
1132
|
raise err
|
1135
1133
|
end
|
1136
1134
|
end
|
@@ -1300,7 +1298,10 @@ module GraphQL
|
|
1300
1298
|
def type_error(type_error, ctx)
|
1301
1299
|
case type_error
|
1302
1300
|
when GraphQL::InvalidNullError
|
1303
|
-
|
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
|
1304
1305
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
1305
1306
|
raise type_error
|
1306
1307
|
when GraphQL::IntegerDecodingError
|
@@ -1368,6 +1369,16 @@ module GraphQL
|
|
1368
1369
|
}.freeze
|
1369
1370
|
end
|
1370
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
|
+
|
1371
1382
|
def tracer(new_tracer, silence_deprecation_warning: false)
|
1372
1383
|
if !silence_deprecation_warning
|
1373
1384
|
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
@@ -1385,14 +1396,22 @@ module GraphQL
|
|
1385
1396
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
1386
1397
|
end
|
1387
1398
|
|
1388
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
1389
|
-
#
|
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
|
1390
1409
|
#
|
1391
1410
|
# @param trace_mod [Module] A module that implements tracing methods
|
1392
1411
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
1393
1412
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1394
1413
|
# @return [void]
|
1395
|
-
# @see GraphQL::Tracing::Trace for available tracing methods
|
1414
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
1396
1415
|
def trace_with(trace_mod, mode: :default, **options)
|
1397
1416
|
if mode.is_a?(Array)
|
1398
1417
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
@@ -1442,29 +1461,36 @@ module GraphQL
|
|
1442
1461
|
#
|
1443
1462
|
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1444
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
|
+
#
|
1445
1467
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1446
1468
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1447
1469
|
# @return [Tracing::Trace]
|
1448
1470
|
def new_trace(mode: nil, **options)
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1459
|
-
options_trace_mode = :default
|
1460
|
-
: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
|
1461
1480
|
end
|
1462
1481
|
else
|
1463
|
-
|
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]
|
1464
1490
|
end
|
1465
1491
|
|
1466
|
-
|
1467
|
-
base_trace_options = trace_options_for(
|
1492
|
+
trace_mode = mode || default_trace_mode
|
1493
|
+
base_trace_options = trace_options_for(trace_mode)
|
1468
1494
|
trace_options = base_trace_options.merge(options)
|
1469
1495
|
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1470
1496
|
trace_class_for_mode.new(**trace_options)
|
@@ -27,6 +27,8 @@ module GraphQL
|
|
27
27
|
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
28
28
|
# @return [Array<Hash>]
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
|
+
errors = nil
|
31
|
+
query.current_trace.begin_validate(query, validate)
|
30
32
|
query.current_trace.validate(validate: validate, query: query) do
|
31
33
|
begin_t = Time.now
|
32
34
|
errors = if validate == false
|
@@ -58,10 +60,13 @@ module GraphQL
|
|
58
60
|
}
|
59
61
|
end
|
60
62
|
rescue GraphQL::ExecutionError => e
|
63
|
+
errors = [e]
|
61
64
|
{
|
62
65
|
remaining_timeout: nil,
|
63
|
-
errors:
|
66
|
+
errors: errors,
|
64
67
|
}
|
68
|
+
ensure
|
69
|
+
query.current_trace.end_validate(query, validate, errors)
|
65
70
|
end
|
66
71
|
|
67
72
|
# Invoked when static validation times out.
|