graphql 2.2.5 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- 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 +2 -3
- data/lib/graphql/language/lexer.rb +29 -28
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +12 -8
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +37 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +2 -2
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +3 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/resolver.rb +9 -5
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +108 -28
- 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 +3 -1
- 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 +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
data/lib/graphql/schema.rb
CHANGED
|
@@ -63,10 +63,6 @@ module GraphQL
|
|
|
63
63
|
# Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
|
|
64
64
|
# (These configurations can be overridden by specific calls to {Schema#execute})
|
|
65
65
|
#
|
|
66
|
-
# Schemas can specify how queries should be executed against them.
|
|
67
|
-
# `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
|
|
68
|
-
# each apply to corresponding root types.
|
|
69
|
-
#
|
|
70
66
|
# @example defining a schema
|
|
71
67
|
# class MySchema < GraphQL::Schema
|
|
72
68
|
# query QueryType
|
|
@@ -162,18 +158,29 @@ module GraphQL
|
|
|
162
158
|
|
|
163
159
|
def trace_class(new_class = nil)
|
|
164
160
|
if new_class
|
|
161
|
+
# If any modules were already added for `:default`,
|
|
162
|
+
# re-apply them here
|
|
163
|
+
mods = trace_modules_for(:default)
|
|
164
|
+
mods.each { |mod| new_class.include(mod) }
|
|
165
165
|
trace_mode(:default, new_class)
|
|
166
166
|
backtrace_class = Class.new(new_class)
|
|
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
|
|
@@ -640,27 +651,39 @@ module GraphQL
|
|
|
640
651
|
end
|
|
641
652
|
end
|
|
642
653
|
|
|
643
|
-
def query_execution_strategy(new_query_execution_strategy = nil)
|
|
654
|
+
def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
|
|
655
|
+
if deprecation_warning
|
|
656
|
+
warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
657
|
+
warn " #{caller(1, 1).first}"
|
|
658
|
+
end
|
|
644
659
|
if new_query_execution_strategy
|
|
645
660
|
@query_execution_strategy = new_query_execution_strategy
|
|
646
661
|
else
|
|
647
|
-
@query_execution_strategy ||
|
|
662
|
+
@query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
648
663
|
end
|
|
649
664
|
end
|
|
650
665
|
|
|
651
|
-
def mutation_execution_strategy(new_mutation_execution_strategy = nil)
|
|
666
|
+
def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
|
|
667
|
+
if deprecation_warning
|
|
668
|
+
warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
669
|
+
warn " #{caller(1, 1).first}"
|
|
670
|
+
end
|
|
652
671
|
if new_mutation_execution_strategy
|
|
653
672
|
@mutation_execution_strategy = new_mutation_execution_strategy
|
|
654
673
|
else
|
|
655
|
-
@mutation_execution_strategy ||
|
|
674
|
+
@mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
656
675
|
end
|
|
657
676
|
end
|
|
658
677
|
|
|
659
|
-
def subscription_execution_strategy(new_subscription_execution_strategy = nil)
|
|
678
|
+
def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
|
|
679
|
+
if deprecation_warning
|
|
680
|
+
warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
681
|
+
warn " #{caller(1, 1).first}"
|
|
682
|
+
end
|
|
660
683
|
if new_subscription_execution_strategy
|
|
661
684
|
@subscription_execution_strategy = new_subscription_execution_strategy
|
|
662
685
|
else
|
|
663
|
-
@subscription_execution_strategy ||
|
|
686
|
+
@subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
664
687
|
end
|
|
665
688
|
end
|
|
666
689
|
|
|
@@ -743,6 +766,7 @@ module GraphQL
|
|
|
743
766
|
|
|
744
767
|
def error_bubbling(new_error_bubbling = nil)
|
|
745
768
|
if !new_error_bubbling.nil?
|
|
769
|
+
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
770
|
@error_bubbling = new_error_bubbling
|
|
747
771
|
else
|
|
748
772
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
|
@@ -814,9 +838,40 @@ module GraphQL
|
|
|
814
838
|
end
|
|
815
839
|
end
|
|
816
840
|
|
|
841
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
|
842
|
+
# @return [Array<Module>] Type definitions added to this schema
|
|
843
|
+
def extra_types(*new_extra_types)
|
|
844
|
+
if new_extra_types.any?
|
|
845
|
+
new_extra_types = new_extra_types.flatten
|
|
846
|
+
@own_extra_types ||= []
|
|
847
|
+
@own_extra_types.concat(new_extra_types)
|
|
848
|
+
end
|
|
849
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
|
850
|
+
if inherited_et
|
|
851
|
+
if @own_extra_types
|
|
852
|
+
inherited_et + @own_extra_types
|
|
853
|
+
else
|
|
854
|
+
inherited_et
|
|
855
|
+
end
|
|
856
|
+
else
|
|
857
|
+
@own_extra_types || EMPTY_ARRAY
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
817
861
|
def orphan_types(*new_orphan_types)
|
|
818
862
|
if new_orphan_types.any?
|
|
819
863
|
new_orphan_types = new_orphan_types.flatten
|
|
864
|
+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
|
865
|
+
if non_object_types.any?
|
|
866
|
+
raise ArgumentError, <<~ERR
|
|
867
|
+
Only object type classes should be added as `orphan_types(...)`.
|
|
868
|
+
|
|
869
|
+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
|
|
870
|
+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
|
|
871
|
+
|
|
872
|
+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
|
873
|
+
ERR
|
|
874
|
+
end
|
|
820
875
|
add_type_and_traverse(new_orphan_types, root: false)
|
|
821
876
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
822
877
|
end
|
|
@@ -1044,6 +1099,12 @@ module GraphQL
|
|
|
1044
1099
|
end
|
|
1045
1100
|
|
|
1046
1101
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1102
|
+
warn <<~WARN
|
|
1103
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
1104
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
|
1105
|
+
|
|
1106
|
+
WARN
|
|
1107
|
+
trace_with(Tracing::LegacyHooksTrace)
|
|
1047
1108
|
own_instrumenters[instrument_step] << instrumenter
|
|
1048
1109
|
end
|
|
1049
1110
|
|
|
@@ -1079,8 +1140,12 @@ module GraphQL
|
|
|
1079
1140
|
}.freeze
|
|
1080
1141
|
end
|
|
1081
1142
|
|
|
1082
|
-
def tracer(new_tracer)
|
|
1083
|
-
|
|
1143
|
+
def tracer(new_tracer, silence_deprecation_warning: false)
|
|
1144
|
+
if !silence_deprecation_warning
|
|
1145
|
+
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
|
1146
|
+
warn " #{caller(1, 1).first}"
|
|
1147
|
+
end
|
|
1148
|
+
default_trace = trace_class_for(:default, build: true)
|
|
1084
1149
|
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
|
1085
1150
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
|
1086
1151
|
end
|
|
@@ -1106,20 +1171,23 @@ module GraphQL
|
|
|
1106
1171
|
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
|
1107
1172
|
tc.include(trace_mod)
|
|
1108
1173
|
own_trace_modules[mode] << trace_mod
|
|
1109
|
-
|
|
1174
|
+
add_trace_options_for(mode, options)
|
|
1110
1175
|
if mode == :default
|
|
1111
1176
|
# This module is being added as a default tracer. If any other mode classes
|
|
1112
1177
|
# have already been created, but get their default behavior from a superclass,
|
|
1113
1178
|
# Then mix this into this schema's subclass.
|
|
1114
1179
|
# (But don't mix it into mode classes that aren't default-based.)
|
|
1115
1180
|
own_trace_modes.each do |other_mode_name, other_mode_class|
|
|
1116
|
-
if other_mode_class < DefaultTraceClass
|
|
1117
|
-
|
|
1181
|
+
if other_mode_class < DefaultTraceClass
|
|
1182
|
+
# Don't add it back to the inheritance tree if it's already there
|
|
1183
|
+
if !(other_mode_class < trace_mod)
|
|
1184
|
+
other_mode_class.include(trace_mod)
|
|
1185
|
+
end
|
|
1186
|
+
# Add any options so they'll be available
|
|
1187
|
+
add_trace_options_for(other_mode_name, options)
|
|
1118
1188
|
end
|
|
1119
1189
|
end
|
|
1120
1190
|
end
|
|
1121
|
-
t_opts = trace_options_for(mode)
|
|
1122
|
-
t_opts.merge!(options)
|
|
1123
1191
|
end
|
|
1124
1192
|
nil
|
|
1125
1193
|
end
|
|
@@ -1129,10 +1197,14 @@ module GraphQL
|
|
|
1129
1197
|
def trace_options_for(mode)
|
|
1130
1198
|
@trace_options_for_mode ||= {}
|
|
1131
1199
|
@trace_options_for_mode[mode] ||= begin
|
|
1200
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
|
1201
|
+
# Mix in the default options in that case.
|
|
1202
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
|
1203
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
|
1132
1204
|
if superclass.respond_to?(:trace_options_for)
|
|
1133
|
-
superclass.trace_options_for(mode).
|
|
1205
|
+
superclass.trace_options_for(mode).merge(default_options)
|
|
1134
1206
|
else
|
|
1135
|
-
|
|
1207
|
+
default_options.dup
|
|
1136
1208
|
end
|
|
1137
1209
|
end
|
|
1138
1210
|
end
|
|
@@ -1155,15 +1227,17 @@ module GraphQL
|
|
|
1155
1227
|
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
|
1156
1228
|
else
|
|
1157
1229
|
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
|
1230
|
+
options_trace_mode = :default
|
|
1158
1231
|
:default_backtrace
|
|
1159
1232
|
end
|
|
1160
1233
|
else
|
|
1161
1234
|
default_trace_mode
|
|
1162
1235
|
end
|
|
1163
1236
|
|
|
1164
|
-
|
|
1237
|
+
options_trace_mode ||= trace_mode
|
|
1238
|
+
base_trace_options = trace_options_for(options_trace_mode)
|
|
1165
1239
|
trace_options = base_trace_options.merge(options)
|
|
1166
|
-
trace_class_for_mode = trace_class_for(trace_mode
|
|
1240
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
|
1167
1241
|
trace_class_for_mode.new(**trace_options)
|
|
1168
1242
|
end
|
|
1169
1243
|
|
|
@@ -1317,6 +1391,12 @@ module GraphQL
|
|
|
1317
1391
|
|
|
1318
1392
|
private
|
|
1319
1393
|
|
|
1394
|
+
def add_trace_options_for(mode, new_options)
|
|
1395
|
+
t_opts = trace_options_for(mode)
|
|
1396
|
+
t_opts.merge!(new_options)
|
|
1397
|
+
nil
|
|
1398
|
+
end
|
|
1399
|
+
|
|
1320
1400
|
# @param t [Module, Array<Module>]
|
|
1321
1401
|
# @return [void]
|
|
1322
1402
|
def add_type_and_traverse(t, root:)
|
|
@@ -1378,7 +1458,7 @@ module GraphQL
|
|
|
1378
1458
|
else
|
|
1379
1459
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
|
1380
1460
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
|
1381
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
|
1461
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
|
1382
1462
|
end
|
|
1383
1463
|
end
|
|
1384
1464
|
@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
|
|
@@ -81,10 +81,12 @@ 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, silence_deprecation_warning: true)
|
|
88
90
|
end
|
|
89
91
|
end
|
|
90
92
|
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
|