graphql 2.2.5 → 2.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast.rb +7 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/runtime.rb +9 -0
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +1 -2
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/schema.rb +28 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +94 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -2
- metadata +6 -19
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a49947bd83b909728424ef018fe58f3e4d7a2d1d1e0fd6688fc48fdf12026aae
|
4
|
+
data.tar.gz: c369b4e0efaa1889cebd8774103e541e2860db4f920962bcc107cdbf3c072ca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '059d1323a0bfe78d29bfdcf2523b26cf3d3a82195d1b8f4a490092a61ce074ae232c7e93e6330b9659d07d28b7cd66a0f2d42e0f38040556ec265773d4a34260'
|
7
|
+
data.tar.gz: 6bd1e3ddc66378e37914fb54806560ca05aeb1294bea4704f53b6445f3b83883ccab45c2d2b0c6b750fd37f4b10aa1351065bb0553f13429ede9343174e1dc62
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
@used_fields = Set.new
|
9
9
|
@used_deprecated_fields = Set.new
|
10
10
|
@used_deprecated_arguments = Set.new
|
11
|
+
@used_deprecated_enum_values = Set.new
|
11
12
|
end
|
12
13
|
|
13
14
|
def on_leave_field(node, parent, visitor)
|
@@ -15,7 +16,7 @@ module GraphQL
|
|
15
16
|
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
|
16
17
|
@used_fields << field
|
17
18
|
@used_deprecated_fields << field if field_defn.deprecation_reason
|
18
|
-
arguments = visitor.query.arguments_for(node,
|
19
|
+
arguments = visitor.query.arguments_for(node, field_defn)
|
19
20
|
# If there was an error when preparing this argument object,
|
20
21
|
# then this might be an error or something:
|
21
22
|
if arguments.respond_to?(:argument_values)
|
@@ -28,6 +29,7 @@ module GraphQL
|
|
28
29
|
used_fields: @used_fields.to_a,
|
29
30
|
used_deprecated_fields: @used_deprecated_fields.to_a,
|
30
31
|
used_deprecated_arguments: @used_deprecated_arguments.to_a,
|
32
|
+
used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
|
31
33
|
}
|
32
34
|
end
|
33
35
|
|
@@ -41,16 +43,39 @@ module GraphQL
|
|
41
43
|
|
42
44
|
next if argument.value.nil?
|
43
45
|
|
44
|
-
|
46
|
+
argument_type = argument.definition.type
|
47
|
+
if argument_type.non_null?
|
48
|
+
argument_type = argument_type.of_type
|
49
|
+
end
|
50
|
+
|
51
|
+
if argument_type.kind.input_object?
|
45
52
|
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
46
|
-
elsif
|
47
|
-
argument
|
48
|
-
|
49
|
-
|
50
|
-
|
53
|
+
elsif argument_type.kind.enum?
|
54
|
+
extract_deprecated_enum_value(argument_type, argument.value)
|
55
|
+
elsif argument_type.list?
|
56
|
+
inner_type = argument_type.unwrap
|
57
|
+
case inner_type.kind
|
58
|
+
when TypeKinds::INPUT_OBJECT
|
59
|
+
argument.value.each do |value|
|
60
|
+
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
61
|
+
end
|
62
|
+
when TypeKinds::ENUM
|
63
|
+
argument.value.each do |value|
|
64
|
+
extract_deprecated_enum_value(inner_type, value)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# Not a kind of input that we track
|
68
|
+
end
|
51
69
|
end
|
52
70
|
end
|
53
71
|
end
|
72
|
+
|
73
|
+
def extract_deprecated_enum_value(enum_type, value)
|
74
|
+
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
|
75
|
+
if enum_value&.deprecation_reason
|
76
|
+
@used_deprecated_enum_values << enum_value.path
|
77
|
+
end
|
78
|
+
end
|
54
79
|
end
|
55
80
|
end
|
56
81
|
end
|
data/lib/graphql/analysis/ast.rb
CHANGED
@@ -6,6 +6,7 @@ require "graphql/analysis/ast/query_complexity"
|
|
6
6
|
require "graphql/analysis/ast/max_query_complexity"
|
7
7
|
require "graphql/analysis/ast/query_depth"
|
8
8
|
require "graphql/analysis/ast/max_query_depth"
|
9
|
+
require "timeout"
|
9
10
|
|
10
11
|
module GraphQL
|
11
12
|
module Analysis
|
@@ -63,7 +64,10 @@ module GraphQL
|
|
63
64
|
analyzers: analyzers_to_run
|
64
65
|
)
|
65
66
|
|
66
|
-
|
67
|
+
# `nil` or `0` causes no timeout
|
68
|
+
Timeout::timeout(query.validate_timeout_remaining) do
|
69
|
+
visitor.visit
|
70
|
+
end
|
67
71
|
|
68
72
|
if visitor.rescued_errors.any?
|
69
73
|
return visitor.rescued_errors
|
@@ -75,6 +79,8 @@ module GraphQL
|
|
75
79
|
[]
|
76
80
|
end
|
77
81
|
end
|
82
|
+
rescue Timeout::Error
|
83
|
+
[GraphQL::AnalysisError.new("Timeout on validation of query")]
|
78
84
|
end
|
79
85
|
|
80
86
|
def analysis_errors(results)
|
@@ -1,13 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
class CoercionError < GraphQL::
|
4
|
-
# @return [Hash] Optional custom data for error objects which will be added
|
5
|
-
# under the `extensions` key.
|
6
|
-
attr_accessor :extensions
|
7
|
-
|
8
|
-
def initialize(message, extensions: nil)
|
9
|
-
@extensions = extensions
|
10
|
-
super(message)
|
11
|
-
end
|
3
|
+
class CoercionError < GraphQL::ExecutionError
|
12
4
|
end
|
13
5
|
end
|
@@ -14,6 +14,11 @@ module GraphQL
|
|
14
14
|
def load
|
15
15
|
@source.load(@key)
|
16
16
|
end
|
17
|
+
|
18
|
+
def load_with_deprecation_warning
|
19
|
+
warn("Returning `.request(...)` from GraphQL::Dataloader is deprecated, use `.load(...)` instead. (See usage of #{@source} with #{@key.inspect}).")
|
20
|
+
load
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -352,6 +352,15 @@ module GraphQL
|
|
352
352
|
end
|
353
353
|
|
354
354
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
355
|
+
if directives.any?
|
356
|
+
# This might be executed in a different context; reset this info
|
357
|
+
runtime_state = get_current_runtime_state
|
358
|
+
runtime_state.current_field = field_defn
|
359
|
+
runtime_state.current_object = object
|
360
|
+
runtime_state.current_arguments = resolved_arguments
|
361
|
+
runtime_state.current_result_name = result_name
|
362
|
+
runtime_state.current_result = selection_result
|
363
|
+
end
|
355
364
|
# Actually call the field resolver and capture the result
|
356
365
|
app_result = begin
|
357
366
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
@@ -37,173 +37,113 @@ module GraphQL
|
|
37
37
|
multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
|
38
38
|
schema = multiplex.schema
|
39
39
|
queries = multiplex.queries
|
40
|
-
query_instrumenters = schema.instrumenters[:query]
|
41
|
-
multiplex_instrumenters = schema.instrumenters[:multiplex]
|
42
40
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
41
|
+
multiplex_analyzers = schema.multiplex_analyzers
|
42
|
+
if multiplex.max_complexity
|
43
|
+
multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
47
|
+
begin
|
48
|
+
# Since this is basically the batching context,
|
49
|
+
# share it for a whole multiplex
|
50
|
+
multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy.new
|
51
|
+
# Do as much eager evaluation of the query as possible
|
52
|
+
results = []
|
53
|
+
queries.each_with_index do |query, idx|
|
54
|
+
if query.subscription? && !query.subscription_update?
|
55
|
+
query.context.namespace(:subscriptions)[:events] = []
|
52
56
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
begin
|
68
|
-
# Although queries in a multiplex _share_ an Interpreter instance,
|
69
|
-
# they also have another item of state, which is private to that query
|
70
|
-
# in particular, assign it here:
|
71
|
-
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
72
|
-
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
73
|
-
|
74
|
-
query.current_trace.execute_query(query: query) do
|
75
|
-
runtime.run_eager
|
76
|
-
end
|
77
|
-
rescue GraphQL::ExecutionError => err
|
78
|
-
query.context.errors << err
|
79
|
-
NO_OPERATION
|
80
|
-
end
|
57
|
+
multiplex.dataloader.append_job {
|
58
|
+
operation = query.selected_operation
|
59
|
+
result = if operation.nil? || !query.valid? || query.context.errors.any?
|
60
|
+
NO_OPERATION
|
61
|
+
else
|
62
|
+
begin
|
63
|
+
# Although queries in a multiplex _share_ an Interpreter instance,
|
64
|
+
# they also have another item of state, which is private to that query
|
65
|
+
# in particular, assign it here:
|
66
|
+
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
67
|
+
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
68
|
+
|
69
|
+
query.current_trace.execute_query(query: query) do
|
70
|
+
runtime.run_eager
|
81
71
|
end
|
82
|
-
|
83
|
-
|
72
|
+
rescue GraphQL::ExecutionError => err
|
73
|
+
query.context.errors << err
|
74
|
+
NO_OPERATION
|
75
|
+
end
|
84
76
|
end
|
77
|
+
results[idx] = result
|
78
|
+
}
|
79
|
+
end
|
85
80
|
|
86
|
-
|
87
|
-
|
88
|
-
# Then, work through lazy results in a breadth-first way
|
89
|
-
multiplex.dataloader.append_job {
|
90
|
-
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
91
|
-
queries = multiplex ? multiplex.queries : [query]
|
92
|
-
final_values = queries.map do |query|
|
93
|
-
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
94
|
-
# it might not be present if the query has an error
|
95
|
-
runtime ? runtime.final_result : nil
|
96
|
-
end
|
97
|
-
final_values.compact!
|
98
|
-
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
99
|
-
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
100
|
-
end
|
101
|
-
}
|
102
|
-
multiplex.dataloader.run
|
81
|
+
multiplex.dataloader.run
|
103
82
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
83
|
+
# Then, work through lazy results in a breadth-first way
|
84
|
+
multiplex.dataloader.append_job {
|
85
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
86
|
+
queries = multiplex ? multiplex.queries : [query]
|
87
|
+
final_values = queries.map do |query|
|
88
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
89
|
+
# it might not be present if the query has an error
|
90
|
+
runtime ? runtime.final_result : nil
|
91
|
+
end
|
92
|
+
final_values.compact!
|
93
|
+
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
94
|
+
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
95
|
+
end
|
96
|
+
}
|
97
|
+
multiplex.dataloader.run
|
119
98
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
99
|
+
# Then, find all errors and assign the result to the query object
|
100
|
+
results.each_with_index do |data_result, idx|
|
101
|
+
query = queries[idx]
|
102
|
+
if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
|
103
|
+
schema.subscriptions.write_subscription(query, events)
|
104
|
+
end
|
105
|
+
# Assign the result so that it can be accessed in instrumentation
|
106
|
+
query.result_values = if data_result.equal?(NO_OPERATION)
|
107
|
+
if !query.valid? || query.context.errors.any?
|
108
|
+
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
109
|
+
{ "errors" => query.static_errors.map(&:to_h) }
|
110
|
+
else
|
111
|
+
data_result
|
112
|
+
end
|
113
|
+
else
|
114
|
+
result = {
|
115
|
+
"data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
116
|
+
}
|
124
117
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
129
|
-
end
|
130
|
-
# Get the Query::Result, not the Hash
|
131
|
-
results[idx] = query.result
|
118
|
+
if query.context.errors.any?
|
119
|
+
error_result = query.context.errors.map(&:to_h)
|
120
|
+
result["errors"] = error_result
|
132
121
|
end
|
133
122
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
queries.map { |q| q.result_values ||= {} }
|
139
|
-
raise
|
140
|
-
ensure
|
141
|
-
queries.map { |query|
|
142
|
-
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
143
|
-
if runtime
|
144
|
-
runtime.delete_all_interpreter_context
|
145
|
-
end
|
146
|
-
}
|
123
|
+
result
|
124
|
+
end
|
125
|
+
if query.context.namespace?(:__query_result_extensions__)
|
126
|
+
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
147
127
|
end
|
128
|
+
# Get the Query::Result, not the Hash
|
129
|
+
results[idx] = query.result
|
148
130
|
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
private
|
154
131
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
132
|
+
results
|
133
|
+
rescue Exception
|
134
|
+
# TODO rescue at a higher level so it will catch errors in analysis, too
|
135
|
+
# Assign values here so that the query's `@executed` becomes true
|
136
|
+
queries.map { |q| q.result_values ||= {} }
|
137
|
+
raise
|
138
|
+
ensure
|
139
|
+
queries.map { |query|
|
140
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
141
|
+
if runtime
|
142
|
+
runtime.delete_all_interpreter_context
|
143
|
+
end
|
166
144
|
}
|
167
|
-
}
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# Call each before hook, and if they all succeed, yield.
|
172
|
-
# If they don't all succeed, call after_ for each one that succeeded.
|
173
|
-
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
|
174
|
-
begin
|
175
|
-
successful = []
|
176
|
-
instrumenters.each do |instrumenter|
|
177
|
-
instrumenter.public_send(before_hook_name, object)
|
178
|
-
successful << instrumenter
|
179
|
-
end
|
180
|
-
|
181
|
-
# if any before hooks raise an exception, quit calling before hooks,
|
182
|
-
# but call the after hooks on anything that succeeded but also
|
183
|
-
# raise the exception that came from the before hook.
|
184
|
-
rescue GraphQL::ExecutionError => err
|
185
|
-
object.context.errors << err
|
186
|
-
rescue => e
|
187
|
-
raise call_after_hooks(successful, object, after_hook_name, e)
|
188
|
-
end
|
189
|
-
|
190
|
-
begin
|
191
|
-
yield # Call the user code
|
192
|
-
ensure
|
193
|
-
ex = call_after_hooks(successful, object, after_hook_name, nil)
|
194
|
-
raise ex if ex
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
199
|
-
instrumenters.reverse_each do |instrumenter|
|
200
|
-
begin
|
201
|
-
instrumenter.public_send(after_hook_name, object)
|
202
|
-
rescue => e
|
203
|
-
ex = e
|
204
145
|
end
|
205
146
|
end
|
206
|
-
ex
|
207
147
|
end
|
208
148
|
end
|
209
149
|
|
@@ -9,13 +9,19 @@ module GraphQL
|
|
9
9
|
|
10
10
|
def __schema
|
11
11
|
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
12
|
-
schema =
|
12
|
+
schema = context.schema
|
13
13
|
schema_type = schema.introspection_system.types["__Schema"]
|
14
|
-
schema_type.wrap(schema,
|
14
|
+
schema_type.wrap(schema, context)
|
15
15
|
end
|
16
16
|
|
17
17
|
def __type(name:)
|
18
|
-
context.warden.reachable_type?(name)
|
18
|
+
if context.warden.reachable_type?(name)
|
19
|
+
context.warden.get_type(name)
|
20
|
+
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
|
21
|
+
type
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -266,8 +266,7 @@ module GraphQL
|
|
266
266
|
end
|
267
267
|
definitions = build_directive_nodes(dirs_to_build)
|
268
268
|
|
269
|
-
type_nodes = build_type_definition_nodes(warden.reachable_types)
|
270
|
-
|
269
|
+
type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
|
271
270
|
if @include_one_of
|
272
271
|
# This may have been set to true when iterating over all types
|
273
272
|
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
|
@@ -35,10 +35,10 @@ module GraphQL
|
|
35
35
|
def load_nodes
|
36
36
|
@nodes ||= begin
|
37
37
|
sliced_nodes = if before && after
|
38
|
-
end_idx = index_from_cursor(before)-
|
38
|
+
end_idx = index_from_cursor(before) - 2
|
39
39
|
end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || []
|
40
40
|
elsif before
|
41
|
-
end_idx = index_from_cursor(before)-2
|
41
|
+
end_idx = index_from_cursor(before) - 2
|
42
42
|
end_idx < 0 ? [] : items[0..end_idx] || []
|
43
43
|
elsif after
|
44
44
|
items[index_from_cursor(after)..-1] || []
|
@@ -56,7 +56,7 @@ module GraphQL
|
|
56
56
|
false
|
57
57
|
end
|
58
58
|
|
59
|
-
@has_next_page = if first
|
59
|
+
@has_next_page = if first_value && first
|
60
60
|
# There are more items after these items
|
61
61
|
sliced_nodes.count > first
|
62
62
|
elsif before
|
@@ -29,14 +29,14 @@ module GraphQL
|
|
29
29
|
|
30
30
|
def has_next_page
|
31
31
|
if @has_next_page.nil?
|
32
|
-
@has_next_page = if
|
33
|
-
true
|
34
|
-
elsif first
|
32
|
+
@has_next_page = if first && first_value
|
35
33
|
if @nodes && @nodes.count < first
|
36
34
|
false
|
37
35
|
else
|
38
36
|
relation_larger_than(sliced_nodes, @sliced_nodes_offset, first)
|
39
37
|
end
|
38
|
+
elsif before_offset && before_offset > 0
|
39
|
+
true
|
40
40
|
else
|
41
41
|
false
|
42
42
|
end
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# @api private
|
16
16
|
class ValidationPipeline
|
17
|
-
attr_reader :max_depth, :max_complexity
|
17
|
+
attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
|
18
18
|
|
19
19
|
def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
|
20
20
|
@validation_errors = []
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
validator = @query.static_validator || @schema.static_validator
|
72
72
|
validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
|
73
73
|
@validation_errors.concat(validation_result[:errors])
|
74
|
-
|
74
|
+
@validate_timeout_remaining = validation_result[:remaining_timeout]
|
75
75
|
if @validation_errors.empty?
|
76
76
|
@validation_errors.concat(@query.variables.errors)
|
77
77
|
end
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
# - Then, fall back to the default value from the query string
|
27
27
|
# If it's still nil, raise an error if it's required.
|
28
28
|
variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
|
29
|
-
if variable_type.nil?
|
29
|
+
if variable_type.nil? || !variable_type.unwrap.kind.input?
|
30
30
|
# Pass -- it will get handled by a validator
|
31
31
|
else
|
32
32
|
variable_name = ast_variable.name
|
@@ -80,12 +80,12 @@ module GraphQL
|
|
80
80
|
else
|
81
81
|
val
|
82
82
|
end
|
83
|
-
end
|
83
|
+
end
|
84
84
|
|
85
85
|
def add_max_errors_reached_message
|
86
86
|
message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
|
87
87
|
validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
88
|
-
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
88
|
+
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -317,7 +317,7 @@ module GraphQL
|
|
317
317
|
end
|
318
318
|
|
319
319
|
def_delegators :validation_pipeline, :validation_errors,
|
320
|
-
:analyzers, :ast_analyzers, :max_depth, :max_complexity
|
320
|
+
:analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
|
321
321
|
|
322
322
|
attr_accessor :analysis_errors
|
323
323
|
def valid?
|
data/lib/graphql/schema.rb
CHANGED
@@ -743,6 +743,7 @@ module GraphQL
|
|
743
743
|
|
744
744
|
def error_bubbling(new_error_bubbling = nil)
|
745
745
|
if !new_error_bubbling.nil?
|
746
|
+
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
|
746
747
|
@error_bubbling = new_error_bubbling
|
747
748
|
else
|
748
749
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
@@ -814,6 +815,26 @@ module GraphQL
|
|
814
815
|
end
|
815
816
|
end
|
816
817
|
|
818
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
819
|
+
# @return [Array<Module>] Type definitions added to this schema
|
820
|
+
def extra_types(*new_extra_types)
|
821
|
+
if new_extra_types.any?
|
822
|
+
new_extra_types = new_extra_types.flatten
|
823
|
+
@own_extra_types ||= []
|
824
|
+
@own_extra_types.concat(new_extra_types)
|
825
|
+
end
|
826
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
827
|
+
if inherited_et
|
828
|
+
if @own_extra_types
|
829
|
+
inherited_et + @own_extra_types
|
830
|
+
else
|
831
|
+
inherited_et
|
832
|
+
end
|
833
|
+
else
|
834
|
+
@own_extra_types || EMPTY_ARRAY
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
817
838
|
def orphan_types(*new_orphan_types)
|
818
839
|
if new_orphan_types.any?
|
819
840
|
new_orphan_types = new_orphan_types.flatten
|
@@ -1044,6 +1065,12 @@ module GraphQL
|
|
1044
1065
|
end
|
1045
1066
|
|
1046
1067
|
def instrument(instrument_step, instrumenter, options = {})
|
1068
|
+
warn <<~WARN
|
1069
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
1070
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
1071
|
+
|
1072
|
+
WARN
|
1073
|
+
trace_with(Tracing::LegacyHooksTrace)
|
1047
1074
|
own_instrumenters[instrument_step] << instrumenter
|
1048
1075
|
end
|
1049
1076
|
|
@@ -1378,7 +1405,7 @@ module GraphQL
|
|
1378
1405
|
else
|
1379
1406
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
1380
1407
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
1381
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
1408
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
1382
1409
|
end
|
1383
1410
|
end
|
1384
1411
|
@lazy_methods
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
110
110
|
# TODO - would be nice to use these to create an error message so the caller knows
|
111
111
|
# that required fields are missing
|
112
112
|
required_field_names = @warden.arguments(type)
|
113
|
-
.select { |argument| argument.type.kind.non_null? &&
|
113
|
+
.select { |argument| argument.type.kind.non_null? && !argument.default_value? }
|
114
114
|
.map!(&:name)
|
115
115
|
|
116
116
|
present_field_names = ast_node.arguments.map(&:name)
|
@@ -122,7 +122,6 @@ module GraphQL
|
|
122
122
|
arg_type = @warden.get_argument(type, name).type
|
123
123
|
recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
|
124
124
|
end
|
125
|
-
|
126
125
|
if type.one_of? && ast_node.arguments.size != 1
|
127
126
|
results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
|
128
127
|
end
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
37
|
required_fields = context.warden.arguments(parent_type)
|
38
|
-
.select{|arg| arg.type.kind.non_null?}
|
38
|
+
.select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
|
39
39
|
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
@@ -28,6 +28,7 @@ module GraphQL
|
|
28
28
|
# @return [Array<Hash>]
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
30
|
query.current_trace.validate(validate: validate, query: query) do
|
31
|
+
begin_t = Time.now
|
31
32
|
errors = if validate == false
|
32
33
|
[]
|
33
34
|
else
|
@@ -52,11 +53,13 @@ module GraphQL
|
|
52
53
|
end
|
53
54
|
|
54
55
|
{
|
56
|
+
remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
|
55
57
|
errors: errors,
|
56
58
|
}
|
57
59
|
end
|
58
60
|
rescue GraphQL::ExecutionError => e
|
59
61
|
{
|
62
|
+
remaining_timeout: nil,
|
60
63
|
errors: [e],
|
61
64
|
}
|
62
65
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
require "securerandom"
|
3
3
|
require "graphql/subscriptions/broadcast_analyzer"
|
4
4
|
require "graphql/subscriptions/event"
|
5
|
-
require "graphql/subscriptions/instrumentation"
|
6
5
|
require "graphql/subscriptions/serialize"
|
7
6
|
require "graphql/subscriptions/action_cable_subscriptions"
|
8
7
|
require "graphql/subscriptions/default_subscription_resolve_extension"
|
@@ -30,8 +29,6 @@ module GraphQL
|
|
30
29
|
raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}"
|
31
30
|
end
|
32
31
|
|
33
|
-
instrumentation = Subscriptions::Instrumentation.new(schema: schema)
|
34
|
-
defn.instrument(:query, instrumentation)
|
35
32
|
options[:schema] = schema
|
36
33
|
schema.subscriptions = self.new(**options)
|
37
34
|
schema.add_subscription_extension_if_necessary
|
@@ -3,20 +3,18 @@
|
|
3
3
|
module GraphQL
|
4
4
|
module Tracing
|
5
5
|
module DataDogTrace
|
6
|
+
# @param tracer [#trace] Deprecated
|
6
7
|
# @param analytics_enabled [Boolean] Deprecated
|
7
8
|
# @param analytics_sample_rate [Float] Deprecated
|
8
|
-
def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service:
|
9
|
+
def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **rest)
|
9
10
|
if tracer.nil?
|
10
11
|
tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
|
11
12
|
end
|
12
13
|
@tracer = tracer
|
13
14
|
|
14
|
-
|
15
|
-
&& Datadog::Contrib::Analytics.respond_to?(:enabled?) \
|
16
|
-
&& Datadog::Contrib::Analytics.respond_to?(:set_sample_rate)
|
17
|
-
|
18
|
-
@analytics_enabled = analytics_available && Datadog::Contrib::Analytics.enabled?(analytics_enabled)
|
15
|
+
@analytics_enabled = analytics_enabled
|
19
16
|
@analytics_sample_rate = analytics_sample_rate
|
17
|
+
|
20
18
|
@service_name = service
|
21
19
|
@has_prepare_span = respond_to?(:prepare_span)
|
22
20
|
super
|
@@ -34,12 +32,9 @@ module GraphQL
|
|
34
32
|
}.each do |trace_method, trace_key|
|
35
33
|
module_eval <<-RUBY, __FILE__, __LINE__
|
36
34
|
def #{trace_method}(**data)
|
37
|
-
@tracer.trace("#{trace_key}", service: @service_name) do |span|
|
38
|
-
|
39
|
-
|
40
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
41
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, '#{trace_method}')
|
42
|
-
end
|
35
|
+
@tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span|
|
36
|
+
span.set_tag('component', 'graphql')
|
37
|
+
span.set_tag('operation', '#{trace_method}')
|
43
38
|
|
44
39
|
#{
|
45
40
|
if trace_method == 'execute_multiplex'
|
@@ -54,10 +49,8 @@ module GraphQL
|
|
54
49
|
end
|
55
50
|
span.resource = resource if resource
|
56
51
|
|
57
|
-
#
|
58
|
-
if @analytics_enabled
|
59
|
-
Datadog::Contrib::Analytics.set_sample_rate(span, @analytics_sample_rate)
|
60
|
-
end
|
52
|
+
# [Deprecated] will be removed in the future
|
53
|
+
span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled
|
61
54
|
RUBY
|
62
55
|
elsif trace_method == 'execute_query'
|
63
56
|
<<-RUBY
|
@@ -89,12 +82,10 @@ module GraphQL
|
|
89
82
|
nil
|
90
83
|
end
|
91
84
|
if platform_key && trace_field
|
92
|
-
@tracer.trace(platform_key, service: @service_name) do |span|
|
93
|
-
span.
|
94
|
-
|
95
|
-
|
96
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
97
|
-
end
|
85
|
+
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
86
|
+
span.set_tag('component', 'graphql')
|
87
|
+
span.set_tag('operation', span_key)
|
88
|
+
|
98
89
|
if @has_prepare_span
|
99
90
|
prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
|
100
91
|
prepare_span(span_key, prepare_span_data, span)
|
@@ -125,12 +116,10 @@ module GraphQL
|
|
125
116
|
|
126
117
|
def authorized_span(span_key, object, type, query)
|
127
118
|
platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
|
128
|
-
@tracer.trace(platform_key, service: @service_name) do |span|
|
129
|
-
span.
|
130
|
-
|
131
|
-
|
132
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
133
|
-
end
|
119
|
+
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
120
|
+
span.set_tag('component', 'graphql')
|
121
|
+
span.set_tag('operation', span_key)
|
122
|
+
|
134
123
|
if @has_prepare_span
|
135
124
|
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
136
125
|
end
|
@@ -158,12 +147,10 @@ module GraphQL
|
|
158
147
|
|
159
148
|
def resolve_type_span(span_key, object, type, query)
|
160
149
|
platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
|
161
|
-
@tracer.trace(platform_key, service: @service_name) do |span|
|
162
|
-
span.
|
163
|
-
|
164
|
-
|
165
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
166
|
-
end
|
150
|
+
@tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
|
151
|
+
span.set_tag('component', 'graphql')
|
152
|
+
span.set_tag('operation', span_key)
|
153
|
+
|
167
154
|
if @has_prepare_span
|
168
155
|
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
169
156
|
end
|
@@ -15,12 +15,9 @@ module GraphQL
|
|
15
15
|
}
|
16
16
|
|
17
17
|
def platform_trace(platform_key, key, data)
|
18
|
-
tracer.trace(platform_key, service:
|
19
|
-
span.
|
20
|
-
|
21
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
22
|
-
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, key)
|
23
|
-
end
|
18
|
+
tracer.trace(platform_key, service: options[:service], type: 'custom') do |span|
|
19
|
+
span.set_tag('component', 'graphql')
|
20
|
+
span.set_tag('operation', key)
|
24
21
|
|
25
22
|
if key == 'execute_multiplex'
|
26
23
|
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
@@ -33,10 +30,8 @@ module GraphQL
|
|
33
30
|
end
|
34
31
|
span.resource = resource if resource
|
35
32
|
|
36
|
-
#
|
37
|
-
if analytics_enabled?
|
38
|
-
Datadog::Contrib::Analytics.set_sample_rate(span, analytics_sample_rate)
|
39
|
-
end
|
33
|
+
# [Deprecated] will be removed in the future
|
34
|
+
span.set_metric('_dd1.sr.eausr', analytics_sample_rate) if analytics_enabled?
|
40
35
|
end
|
41
36
|
|
42
37
|
if key == 'execute_query'
|
@@ -51,10 +46,6 @@ module GraphQL
|
|
51
46
|
end
|
52
47
|
end
|
53
48
|
|
54
|
-
def service_name
|
55
|
-
options.fetch(:service, 'ruby-graphql')
|
56
|
-
end
|
57
|
-
|
58
49
|
# Implement this method in a subclass to apply custom tags to datadog spans
|
59
50
|
# @param key [String] The event being traced
|
60
51
|
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
@@ -65,18 +56,13 @@ module GraphQL
|
|
65
56
|
def tracer
|
66
57
|
default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
|
67
58
|
|
59
|
+
# [Deprecated] options[:tracer] will be removed in the future
|
68
60
|
options.fetch(:tracer, default_tracer)
|
69
61
|
end
|
70
62
|
|
71
|
-
def analytics_available?
|
72
|
-
defined?(Datadog::Contrib::Analytics) \
|
73
|
-
&& Datadog::Contrib::Analytics.respond_to?(:enabled?) \
|
74
|
-
&& Datadog::Contrib::Analytics.respond_to?(:set_sample_rate)
|
75
|
-
end
|
76
|
-
|
77
63
|
def analytics_enabled?
|
78
64
|
# [Deprecated] options[:analytics_enabled] will be removed in the future
|
79
|
-
|
65
|
+
options.fetch(:analytics_enabled, false)
|
80
66
|
end
|
81
67
|
|
82
68
|
def analytics_sample_rate
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Tracing
|
4
|
+
module LegacyHooksTrace
|
5
|
+
def execute_multiplex(multiplex:)
|
6
|
+
multiplex_instrumenters = multiplex.schema.instrumenters[:multiplex]
|
7
|
+
query_instrumenters = multiplex.schema.instrumenters[:query]
|
8
|
+
# First, run multiplex instrumentation, then query instrumentation for each query
|
9
|
+
RunHooks.call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
|
10
|
+
RunHooks.each_query_call_hooks(query_instrumenters, multiplex.queries) do
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module RunHooks
|
17
|
+
module_function
|
18
|
+
# Call the before_ hooks of each query,
|
19
|
+
# Then yield if no errors.
|
20
|
+
# `call_hooks` takes care of appropriate cleanup.
|
21
|
+
def each_query_call_hooks(instrumenters, queries, i = 0)
|
22
|
+
if i >= queries.length
|
23
|
+
yield
|
24
|
+
else
|
25
|
+
query = queries[i]
|
26
|
+
call_hooks(instrumenters, query, :before_query, :after_query) {
|
27
|
+
each_query_call_hooks(instrumenters, queries, i + 1) {
|
28
|
+
yield
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Call each before hook, and if they all succeed, yield.
|
35
|
+
# If they don't all succeed, call after_ for each one that succeeded.
|
36
|
+
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
|
37
|
+
begin
|
38
|
+
successful = []
|
39
|
+
instrumenters.each do |instrumenter|
|
40
|
+
instrumenter.public_send(before_hook_name, object)
|
41
|
+
successful << instrumenter
|
42
|
+
end
|
43
|
+
|
44
|
+
# if any before hooks raise an exception, quit calling before hooks,
|
45
|
+
# but call the after hooks on anything that succeeded but also
|
46
|
+
# raise the exception that came from the before hook.
|
47
|
+
rescue GraphQL::ExecutionError => err
|
48
|
+
object.context.errors << err
|
49
|
+
rescue => e
|
50
|
+
raise call_after_hooks(successful, object, after_hook_name, e)
|
51
|
+
end
|
52
|
+
|
53
|
+
begin
|
54
|
+
yield # Call the user code
|
55
|
+
ensure
|
56
|
+
ex = call_after_hooks(successful, object, after_hook_name, nil)
|
57
|
+
raise ex if ex
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
62
|
+
instrumenters.reverse_each do |instrumenter|
|
63
|
+
begin
|
64
|
+
instrumenter.public_send(after_hook_name, object)
|
65
|
+
rescue => e
|
66
|
+
ex = e
|
67
|
+
end
|
68
|
+
end
|
69
|
+
ex
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -81,8 +81,10 @@ module GraphQL
|
|
81
81
|
trace_name = tracing_name.sub("Tracing", "Trace")
|
82
82
|
if GraphQL::Tracing.const_defined?(trace_name, false)
|
83
83
|
trace_module = GraphQL::Tracing.const_get(trace_name)
|
84
|
+
warn("`use(#{self.name})` is deprecated, use the equivalent `trace_with(#{trace_module.name})` instead. More info: https://graphql-ruby.org/queries/tracing.html")
|
84
85
|
schema_defn.trace_with(trace_module, **options)
|
85
86
|
else
|
87
|
+
warn("`use(#{self.name})` and `Tracing::PlatformTracing` are deprecated. Use a `trace_with(...)` module instead. More info: https://graphql-ruby.org/queries/tracing.html. Please open an issue on the GraphQL-Ruby repo if you want to discuss further!")
|
86
88
|
tracer = self.new(**options)
|
87
89
|
schema_defn.tracer(tracer)
|
88
90
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
module Tracing
|
5
|
-
|
5
|
+
module PrometheusTrace
|
6
6
|
class GraphQLCollector < ::PrometheusExporter::Server::TypeCollector
|
7
7
|
def initialize
|
8
8
|
@graphql_gauge = PrometheusExporter::Metric::Base.default_aggregation.new(
|
@@ -28,5 +28,7 @@ module GraphQL
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
# Backwards-compat:
|
32
|
+
PrometheusTracing::GraphQLCollector = PrometheusTrace::GraphQLCollector
|
31
33
|
end
|
32
34
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Tracing
|
5
|
+
module SentryTrace
|
6
|
+
include PlatformTrace
|
7
|
+
|
8
|
+
{
|
9
|
+
"lex" => "graphql.lex",
|
10
|
+
"parse" => "graphql.parse",
|
11
|
+
"validate" => "graphql.validate",
|
12
|
+
"analyze_query" => "graphql.analyze",
|
13
|
+
"analyze_multiplex" => "graphql.analyze_multiplex",
|
14
|
+
"execute_multiplex" => "graphql.execute_multiplex",
|
15
|
+
"execute_query" => "graphql.execute",
|
16
|
+
"execute_query_lazy" => "graphql.execute"
|
17
|
+
}.each do |trace_method, platform_key|
|
18
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
19
|
+
def #{trace_method}(**data, &block)
|
20
|
+
instrument_execution("#{platform_key}", "#{trace_method}", data, &block)
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
end
|
24
|
+
|
25
|
+
def platform_execute_field(platform_key, &block)
|
26
|
+
instrument_execution(platform_key, "execute_field", &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def platform_execute_field_lazy(platform_key, &block)
|
30
|
+
instrument_execution(platform_key, "execute_field_lazy", &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def platform_authorized(platform_key, &block)
|
34
|
+
instrument_execution(platform_key, "authorized", &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def platform_authorized_lazy(platform_key, &block)
|
38
|
+
instrument_execution(platform_key, "authorized_lazy", &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def platform_resolve_type(platform_key, &block)
|
42
|
+
instrument_execution(platform_key, "resolve_type", &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def platform_resolve_type_lazy(platform_key, &block)
|
46
|
+
instrument_execution(platform_key, "resolve_type_lazy", &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def platform_field_key(field)
|
50
|
+
"graphql.field.#{field.path}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def platform_authorized_key(type)
|
54
|
+
"graphql.authorized.#{type.graphql_name}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def platform_resolve_type_key(type)
|
58
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def instrument_execution(platform_key, trace_method, data=nil, &block)
|
64
|
+
return yield unless Sentry.initialized?
|
65
|
+
|
66
|
+
Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
|
67
|
+
result = block.call
|
68
|
+
span.finish
|
69
|
+
|
70
|
+
if trace_method == "execute_multiplex" && data.key?(:multiplex)
|
71
|
+
operation_names = data[:multiplex].queries.map{|q| operation_name(q) }
|
72
|
+
span.set_description(operation_names.join(", "))
|
73
|
+
elsif trace_method == "execute_query" && data.key?(:query)
|
74
|
+
span.set_description(operation_name(data[:query]))
|
75
|
+
span.set_data('graphql.document', data[:query].query_string)
|
76
|
+
span.set_data('graphql.operation.name', data[:query].selected_operation_name) if data[:query].selected_operation_name
|
77
|
+
span.set_data('graphql.operation.type', data[:query].selected_operation.operation_type)
|
78
|
+
end
|
79
|
+
|
80
|
+
result
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def operation_name(query)
|
85
|
+
selected_op = query.selected_operation
|
86
|
+
if selected_op
|
87
|
+
[selected_op.operation_type, selected_op.name].compact.join(' ')
|
88
|
+
else
|
89
|
+
'GraphQL Operation'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/graphql/tracing.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/tracing/trace"
|
3
3
|
require "graphql/tracing/legacy_trace"
|
4
|
+
require "graphql/tracing/legacy_hooks_trace"
|
4
5
|
|
5
6
|
# Legacy tracing:
|
6
7
|
require "graphql/tracing/active_support_notifications_tracing"
|
@@ -21,11 +22,12 @@ require "graphql/tracing/appsignal_trace"
|
|
21
22
|
require "graphql/tracing/data_dog_trace"
|
22
23
|
require "graphql/tracing/new_relic_trace"
|
23
24
|
require "graphql/tracing/notifications_trace"
|
25
|
+
require "graphql/tracing/sentry_trace"
|
24
26
|
require "graphql/tracing/scout_trace"
|
25
27
|
require "graphql/tracing/statsd_trace"
|
26
28
|
require "graphql/tracing/prometheus_trace"
|
27
29
|
if defined?(PrometheusExporter::Server)
|
28
|
-
require "graphql/tracing/
|
30
|
+
require "graphql/tracing/prometheus_trace/graphql_collector"
|
29
31
|
end
|
30
32
|
|
31
33
|
module GraphQL
|
data/lib/graphql/version.rb
CHANGED
data/lib/graphql.rb
CHANGED
@@ -42,8 +42,8 @@ This is probably a bug in GraphQL-Ruby, please report this error on GitHub: http
|
|
42
42
|
# Turn a query string or schema definition into an AST
|
43
43
|
# @param graphql_string [String] a GraphQL query string or schema definition
|
44
44
|
# @return [GraphQL::Language::Nodes::Document]
|
45
|
-
def self.parse(graphql_string, trace: GraphQL::Tracing::NullTrace)
|
46
|
-
default_parser.parse(graphql_string, trace: trace)
|
45
|
+
def self.parse(graphql_string, trace: GraphQL::Tracing::NullTrace, filename: nil)
|
46
|
+
default_parser.parse(graphql_string, trace: trace, filename: filename)
|
47
47
|
end
|
48
48
|
|
49
49
|
# Read the contents of `filename` and parse them as GraphQL
|
@@ -60,6 +60,7 @@ This is probably a bug in GraphQL-Ruby, please report this error on GitHub: http
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def self.parse_with_racc(string, filename: nil, trace: GraphQL::Tracing::NullTrace)
|
63
|
+
warn "`GraphQL.parse_with_racc` is deprecated; GraphQL-Ruby no longer uses racc for parsing. Call `GraphQL.parse` or `GraphQL::Language::Parser.parse` instead."
|
63
64
|
GraphQL::Language::Parser.parse(string, filename: filename, trace: trace)
|
64
65
|
end
|
65
66
|
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: racc
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.4'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.4'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: benchmark-ips
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -550,7 +536,6 @@ files:
|
|
550
536
|
- lib/graphql/subscriptions/broadcast_analyzer.rb
|
551
537
|
- lib/graphql/subscriptions/default_subscription_resolve_extension.rb
|
552
538
|
- lib/graphql/subscriptions/event.rb
|
553
|
-
- lib/graphql/subscriptions/instrumentation.rb
|
554
539
|
- lib/graphql/subscriptions/serialize.rb
|
555
540
|
- lib/graphql/testing.rb
|
556
541
|
- lib/graphql/testing/helpers.rb
|
@@ -563,6 +548,7 @@ files:
|
|
563
548
|
- lib/graphql/tracing/appsignal_tracing.rb
|
564
549
|
- lib/graphql/tracing/data_dog_trace.rb
|
565
550
|
- lib/graphql/tracing/data_dog_tracing.rb
|
551
|
+
- lib/graphql/tracing/legacy_hooks_trace.rb
|
566
552
|
- lib/graphql/tracing/legacy_trace.rb
|
567
553
|
- lib/graphql/tracing/new_relic_trace.rb
|
568
554
|
- lib/graphql/tracing/new_relic_tracing.rb
|
@@ -571,10 +557,11 @@ files:
|
|
571
557
|
- lib/graphql/tracing/platform_trace.rb
|
572
558
|
- lib/graphql/tracing/platform_tracing.rb
|
573
559
|
- lib/graphql/tracing/prometheus_trace.rb
|
560
|
+
- lib/graphql/tracing/prometheus_trace/graphql_collector.rb
|
574
561
|
- lib/graphql/tracing/prometheus_tracing.rb
|
575
|
-
- lib/graphql/tracing/prometheus_tracing/graphql_collector.rb
|
576
562
|
- lib/graphql/tracing/scout_trace.rb
|
577
563
|
- lib/graphql/tracing/scout_tracing.rb
|
564
|
+
- lib/graphql/tracing/sentry_trace.rb
|
578
565
|
- lib/graphql/tracing/statsd_trace.rb
|
579
566
|
- lib/graphql/tracing/statsd_tracing.rb
|
580
567
|
- lib/graphql/tracing/trace.rb
|
@@ -630,7 +617,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
630
617
|
- !ruby/object:Gem::Version
|
631
618
|
version: '0'
|
632
619
|
requirements: []
|
633
|
-
rubygems_version: 3.
|
620
|
+
rubygems_version: 3.5.5
|
634
621
|
signing_key:
|
635
622
|
specification_version: 4
|
636
623
|
summary: A GraphQL language and runtime for Ruby
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
class Subscriptions
|
4
|
-
# Wrap the root fields of the subscription type with special logic for:
|
5
|
-
# - Registering the subscription during the first execution
|
6
|
-
# - Evaluating the triggered portion(s) of the subscription during later execution
|
7
|
-
class Instrumentation
|
8
|
-
def initialize(schema:)
|
9
|
-
@schema = schema
|
10
|
-
end
|
11
|
-
|
12
|
-
# If needed, prepare to gather events which this query subscribes to
|
13
|
-
def before_query(query)
|
14
|
-
if query.subscription? && !query.subscription_update?
|
15
|
-
query.context.namespace(:subscriptions)[:events] = []
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# After checking the root fields, pass the gathered events to the store
|
20
|
-
def after_query(query)
|
21
|
-
events = query.context.namespace(:subscriptions)[:events]
|
22
|
-
if events && events.any?
|
23
|
-
@schema.subscriptions.write_subscription(query, events)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|