graphql 2.2.5 → 2.2.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/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/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +11 -1
- data/lib/graphql/language/printer.rb +4 -0
- 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/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/field.rb +31 -30
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/resolver.rb +9 -5
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +70 -17
- 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/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +8 -4
- 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 +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -2
- metadata +24 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
data/lib/graphql/schema/field.rb
CHANGED
@@ -483,41 +483,24 @@ module GraphQL
|
|
483
483
|
metadata_complexity = 0
|
484
484
|
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
485
485
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
486
|
+
lookahead.selections.each do |next_lookahead|
|
487
|
+
# this includes `pageInfo`, `nodes` and `edges` and any custom fields
|
488
|
+
# TODO this doesn't support procs yet -- unlikely to need it.
|
489
|
+
metadata_complexity += next_lookahead.field.complexity
|
490
|
+
if next_lookahead.name != :nodes && next_lookahead.name != :edges
|
491
|
+
# subfields, eg, for pageInfo -- assumes no subselections
|
492
|
+
metadata_complexity += next_lookahead.selections.size
|
493
|
+
end
|
493
494
|
end
|
494
495
|
|
495
|
-
nodes_edges_complexity = 0
|
496
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
497
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
498
|
-
|
499
496
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
500
|
-
items_complexity = child_complexity - metadata_complexity
|
501
|
-
|
502
|
-
|
497
|
+
items_complexity = child_complexity - metadata_complexity
|
498
|
+
subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
|
499
|
+
# Apply this field's own complexity
|
500
|
+
apply_own_complexity_to(subfields_complexity, query, nodes)
|
503
501
|
end
|
504
502
|
else
|
505
|
-
|
506
|
-
case defined_complexity
|
507
|
-
when Proc
|
508
|
-
arguments = query.arguments_for(nodes.first, self)
|
509
|
-
if arguments.is_a?(GraphQL::ExecutionError)
|
510
|
-
return child_complexity
|
511
|
-
elsif arguments.respond_to?(:keyword_arguments)
|
512
|
-
arguments = arguments.keyword_arguments
|
513
|
-
end
|
514
|
-
|
515
|
-
defined_complexity.call(query.context, arguments, child_complexity)
|
516
|
-
when Numeric
|
517
|
-
defined_complexity + child_complexity
|
518
|
-
else
|
519
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
520
|
-
end
|
503
|
+
apply_own_complexity_to(child_complexity, query, nodes)
|
521
504
|
end
|
522
505
|
end
|
523
506
|
|
@@ -882,6 +865,24 @@ ERR
|
|
882
865
|
yield(obj, args)
|
883
866
|
end
|
884
867
|
end
|
868
|
+
|
869
|
+
def apply_own_complexity_to(child_complexity, query, nodes)
|
870
|
+
case (own_complexity = complexity)
|
871
|
+
when Numeric
|
872
|
+
own_complexity + child_complexity
|
873
|
+
when Proc
|
874
|
+
arguments = query.arguments_for(nodes.first, self)
|
875
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
876
|
+
return child_complexity
|
877
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
878
|
+
arguments = arguments.keyword_arguments
|
879
|
+
end
|
880
|
+
|
881
|
+
own_complexity.call(query.context, arguments, child_complexity)
|
882
|
+
else
|
883
|
+
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
884
|
+
end
|
885
|
+
end
|
885
886
|
end
|
886
887
|
end
|
887
888
|
end
|
@@ -69,7 +69,11 @@ module GraphQL
|
|
69
69
|
end
|
70
70
|
elsif child_class < GraphQL::Schema::Object
|
71
71
|
# This is being included into an object type, make sure it's using `implements(...)`
|
72
|
-
backtrace_line =
|
72
|
+
backtrace_line = caller_locations(0, 10).find do |location|
|
73
|
+
location.base_label == "implements" &&
|
74
|
+
location.path.end_with?("schema/member/has_interfaces.rb")
|
75
|
+
end
|
76
|
+
|
73
77
|
if !backtrace_line
|
74
78
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
75
79
|
end
|
@@ -166,11 +166,15 @@ module GraphQL
|
|
166
166
|
args.each_value do |argument|
|
167
167
|
arg_keyword = argument.keyword
|
168
168
|
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
169
|
-
|
170
|
-
if
|
171
|
-
return
|
172
|
-
|
173
|
-
|
169
|
+
auth_result = argument.authorized?(self, arg_value, context)
|
170
|
+
if auth_result.is_a?(Array)
|
171
|
+
# only return this second value if the application returned a second value
|
172
|
+
arg_auth, err = auth_result
|
173
|
+
if !arg_auth
|
174
|
+
return arg_auth, err
|
175
|
+
end
|
176
|
+
elsif auth_result == false
|
177
|
+
return auth_result
|
174
178
|
end
|
175
179
|
else
|
176
180
|
true
|
data/lib/graphql/schema.rb
CHANGED
@@ -167,13 +167,20 @@ module GraphQL
|
|
167
167
|
backtrace_class.include(GraphQL::Backtrace::Trace)
|
168
168
|
trace_mode(:default_backtrace, backtrace_class)
|
169
169
|
end
|
170
|
-
trace_class_for(:default)
|
170
|
+
trace_class_for(:default, build: true)
|
171
171
|
end
|
172
172
|
|
173
173
|
# @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
|
174
|
-
def trace_class_for(mode, build:
|
175
|
-
own_trace_modes[mode]
|
176
|
-
|
174
|
+
def trace_class_for(mode, build: false)
|
175
|
+
if (trace_class = own_trace_modes[mode])
|
176
|
+
trace_class
|
177
|
+
elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
|
178
|
+
trace_class
|
179
|
+
elsif build
|
180
|
+
own_trace_modes[mode] = build_trace_mode(mode)
|
181
|
+
else
|
182
|
+
nil
|
183
|
+
end
|
177
184
|
end
|
178
185
|
|
179
186
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
@@ -211,20 +218,24 @@ module GraphQL
|
|
211
218
|
include DefaultTraceClass
|
212
219
|
end
|
213
220
|
when :default_backtrace
|
214
|
-
schema_base_class = trace_class_for(:default)
|
221
|
+
schema_base_class = trace_class_for(:default, build: true)
|
215
222
|
Class.new(schema_base_class) do
|
216
223
|
include(GraphQL::Backtrace::Trace)
|
217
224
|
end
|
218
225
|
else
|
219
226
|
# First, see if the superclass has a custom-defined class for this.
|
220
227
|
# Then, if it doesn't, use this class's default trace
|
221
|
-
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode
|
228
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
|
222
229
|
# Prepare the default trace class if it hasn't been initialized yet
|
223
230
|
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
224
231
|
mods = trace_modules_for(mode)
|
225
232
|
if base_class < DefaultTraceClass
|
226
233
|
mods = trace_modules_for(:default) + mods
|
227
234
|
end
|
235
|
+
# Copy the existing default options into this mode's options
|
236
|
+
default_options = trace_options_for(:default)
|
237
|
+
add_trace_options_for(mode, default_options)
|
238
|
+
|
228
239
|
Class.new(base_class) do
|
229
240
|
mods.any? && include(*mods)
|
230
241
|
end
|
@@ -743,6 +754,7 @@ module GraphQL
|
|
743
754
|
|
744
755
|
def error_bubbling(new_error_bubbling = nil)
|
745
756
|
if !new_error_bubbling.nil?
|
757
|
+
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
758
|
@error_bubbling = new_error_bubbling
|
747
759
|
else
|
748
760
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
@@ -814,6 +826,26 @@ module GraphQL
|
|
814
826
|
end
|
815
827
|
end
|
816
828
|
|
829
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
830
|
+
# @return [Array<Module>] Type definitions added to this schema
|
831
|
+
def extra_types(*new_extra_types)
|
832
|
+
if new_extra_types.any?
|
833
|
+
new_extra_types = new_extra_types.flatten
|
834
|
+
@own_extra_types ||= []
|
835
|
+
@own_extra_types.concat(new_extra_types)
|
836
|
+
end
|
837
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
838
|
+
if inherited_et
|
839
|
+
if @own_extra_types
|
840
|
+
inherited_et + @own_extra_types
|
841
|
+
else
|
842
|
+
inherited_et
|
843
|
+
end
|
844
|
+
else
|
845
|
+
@own_extra_types || EMPTY_ARRAY
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
817
849
|
def orphan_types(*new_orphan_types)
|
818
850
|
if new_orphan_types.any?
|
819
851
|
new_orphan_types = new_orphan_types.flatten
|
@@ -1044,6 +1076,12 @@ module GraphQL
|
|
1044
1076
|
end
|
1045
1077
|
|
1046
1078
|
def instrument(instrument_step, instrumenter, options = {})
|
1079
|
+
warn <<~WARN
|
1080
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
1081
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
1082
|
+
|
1083
|
+
WARN
|
1084
|
+
trace_with(Tracing::LegacyHooksTrace)
|
1047
1085
|
own_instrumenters[instrument_step] << instrumenter
|
1048
1086
|
end
|
1049
1087
|
|
@@ -1080,7 +1118,7 @@ module GraphQL
|
|
1080
1118
|
end
|
1081
1119
|
|
1082
1120
|
def tracer(new_tracer)
|
1083
|
-
default_trace = trace_class_for(:default)
|
1121
|
+
default_trace = trace_class_for(:default, build: true)
|
1084
1122
|
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
1085
1123
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
1086
1124
|
end
|
@@ -1106,20 +1144,23 @@ module GraphQL
|
|
1106
1144
|
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1107
1145
|
tc.include(trace_mod)
|
1108
1146
|
own_trace_modules[mode] << trace_mod
|
1109
|
-
|
1147
|
+
add_trace_options_for(mode, options)
|
1110
1148
|
if mode == :default
|
1111
1149
|
# This module is being added as a default tracer. If any other mode classes
|
1112
1150
|
# have already been created, but get their default behavior from a superclass,
|
1113
1151
|
# Then mix this into this schema's subclass.
|
1114
1152
|
# (But don't mix it into mode classes that aren't default-based.)
|
1115
1153
|
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1116
|
-
if other_mode_class < DefaultTraceClass
|
1117
|
-
|
1154
|
+
if other_mode_class < DefaultTraceClass
|
1155
|
+
# Don't add it back to the inheritance tree if it's already there
|
1156
|
+
if !(other_mode_class < trace_mod)
|
1157
|
+
other_mode_class.include(trace_mod)
|
1158
|
+
end
|
1159
|
+
# Add any options so they'll be available
|
1160
|
+
add_trace_options_for(other_mode_name, options)
|
1118
1161
|
end
|
1119
1162
|
end
|
1120
1163
|
end
|
1121
|
-
t_opts = trace_options_for(mode)
|
1122
|
-
t_opts.merge!(options)
|
1123
1164
|
end
|
1124
1165
|
nil
|
1125
1166
|
end
|
@@ -1129,10 +1170,14 @@ module GraphQL
|
|
1129
1170
|
def trace_options_for(mode)
|
1130
1171
|
@trace_options_for_mode ||= {}
|
1131
1172
|
@trace_options_for_mode[mode] ||= begin
|
1173
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
1174
|
+
# Mix in the default options in that case.
|
1175
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
1176
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
1132
1177
|
if superclass.respond_to?(:trace_options_for)
|
1133
|
-
superclass.trace_options_for(mode).
|
1178
|
+
superclass.trace_options_for(mode).merge(default_options)
|
1134
1179
|
else
|
1135
|
-
|
1180
|
+
default_options.dup
|
1136
1181
|
end
|
1137
1182
|
end
|
1138
1183
|
end
|
@@ -1155,15 +1200,17 @@ module GraphQL
|
|
1155
1200
|
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1156
1201
|
else
|
1157
1202
|
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1203
|
+
options_trace_mode = :default
|
1158
1204
|
:default_backtrace
|
1159
1205
|
end
|
1160
1206
|
else
|
1161
1207
|
default_trace_mode
|
1162
1208
|
end
|
1163
1209
|
|
1164
|
-
|
1210
|
+
options_trace_mode ||= trace_mode
|
1211
|
+
base_trace_options = trace_options_for(options_trace_mode)
|
1165
1212
|
trace_options = base_trace_options.merge(options)
|
1166
|
-
trace_class_for_mode = trace_class_for(trace_mode
|
1213
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1167
1214
|
trace_class_for_mode.new(**trace_options)
|
1168
1215
|
end
|
1169
1216
|
|
@@ -1317,6 +1364,12 @@ module GraphQL
|
|
1317
1364
|
|
1318
1365
|
private
|
1319
1366
|
|
1367
|
+
def add_trace_options_for(mode, new_options)
|
1368
|
+
t_opts = trace_options_for(mode)
|
1369
|
+
t_opts.merge!(new_options)
|
1370
|
+
nil
|
1371
|
+
end
|
1372
|
+
|
1320
1373
|
# @param t [Module, Array<Module>]
|
1321
1374
|
# @return [void]
|
1322
1375
|
def add_type_and_traverse(t, root:)
|
@@ -1378,7 +1431,7 @@ module GraphQL
|
|
1378
1431
|
else
|
1379
1432
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
1380
1433
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
1381
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
1434
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
1382
1435
|
end
|
1383
1436
|
end
|
1384
1437
|
@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
|
@@ -148,6 +148,8 @@ module GraphQL
|
|
148
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
149
149
|
elsif obj.is_a?(OpenStruct)
|
150
150
|
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
|
151
|
+
elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
|
152
|
+
dump_value(obj.to_a)
|
151
153
|
else
|
152
154
|
obj
|
153
155
|
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
|
@@ -5,10 +5,7 @@ module GraphQL
|
|
5
5
|
# @param schema_class [Class<GraphQL::Schema>]
|
6
6
|
# @return [Module] A helpers module which always uses the given schema
|
7
7
|
def self.for(schema_class)
|
8
|
-
|
9
|
-
include SchemaHelpers
|
10
|
-
@@schema_class_for_helpers = schema_class
|
11
|
-
end
|
8
|
+
SchemaHelpers.for(schema_class)
|
12
9
|
end
|
13
10
|
|
14
11
|
class Error < GraphQL::Error
|
@@ -119,6 +116,13 @@ module GraphQL
|
|
119
116
|
# schema will be added later
|
120
117
|
super(nil, *args, **kwargs, &block)
|
121
118
|
end
|
119
|
+
|
120
|
+
def self.for(schema_class)
|
121
|
+
Module.new do
|
122
|
+
include SchemaHelpers
|
123
|
+
@@schema_class_for_helpers = schema_class
|
124
|
+
end
|
125
|
+
end
|
122
126
|
end
|
123
127
|
end
|
124
128
|
end
|
@@ -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
|