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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/field_usage.rb +32 -7
  3. data/lib/graphql/analysis/ast/visitor.rb +8 -0
  4. data/lib/graphql/analysis/ast.rb +10 -1
  5. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  6. data/lib/graphql/coercion_error.rb +1 -9
  7. data/lib/graphql/dataloader/request.rb +5 -0
  8. data/lib/graphql/execution/interpreter/runtime.rb +9 -0
  9. data/lib/graphql/execution/interpreter.rb +90 -150
  10. data/lib/graphql/introspection/entry_points.rb +9 -3
  11. data/lib/graphql/introspection/schema_type.rb +3 -1
  12. data/lib/graphql/language/document_from_schema_definition.rb +2 -3
  13. data/lib/graphql/language/lexer.rb +29 -28
  14. data/lib/graphql/language/nodes.rb +1 -1
  15. data/lib/graphql/language/parser.rb +12 -8
  16. data/lib/graphql/language/printer.rb +4 -0
  17. data/lib/graphql/language.rb +37 -0
  18. data/lib/graphql/pagination/array_connection.rb +6 -6
  19. data/lib/graphql/query/context.rb +30 -33
  20. data/lib/graphql/query/validation_pipeline.rb +2 -2
  21. data/lib/graphql/query/variables.rb +3 -3
  22. data/lib/graphql/query.rb +2 -2
  23. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  24. data/lib/graphql/schema/build_from_definition.rb +3 -1
  25. data/lib/graphql/schema/field.rb +33 -30
  26. data/lib/graphql/schema/interface.rb +5 -1
  27. data/lib/graphql/schema/loader.rb +2 -1
  28. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  29. data/lib/graphql/schema/resolver.rb +9 -5
  30. data/lib/graphql/schema/unique_within_type.rb +1 -1
  31. data/lib/graphql/schema.rb +108 -28
  32. data/lib/graphql/static_validation/literal_validator.rb +1 -2
  33. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  34. data/lib/graphql/static_validation/validator.rb +3 -0
  35. data/lib/graphql/subscriptions/serialize.rb +2 -0
  36. data/lib/graphql/subscriptions.rb +0 -3
  37. data/lib/graphql/testing/helpers.rb +8 -4
  38. data/lib/graphql/tracing/data_dog_trace.rb +21 -34
  39. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  40. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  41. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  42. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  43. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  44. data/lib/graphql/tracing.rb +3 -1
  45. data/lib/graphql/version.rb +1 -1
  46. data/lib/graphql.rb +3 -2
  47. metadata +38 -23
  48. data/lib/graphql/schema/base_64_bp.rb +0 -26
  49. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -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: true)
175
- own_trace_modes[mode] ||
176
- (superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode, build: build) : (build ? (own_trace_modes[mode] = build_trace_mode(mode)) : nil))
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, build: false)) || trace_class_for(:default)
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 || find_inherited_value(:query_execution_strategy, self.default_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 || find_inherited_value(:mutation_execution_strategy, self.default_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 || find_inherited_value(:subscription_execution_strategy, self.default_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
- default_trace = trace_class_for(:default)
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 && !(other_mode_class < trace_mod)
1117
- other_mode_class.include(trace_mod)
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).dup
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
- base_trace_options = trace_options_for(trace_mode)
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) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
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, :load)
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? && @warden.get_argument(type, argument.name) }
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
- Module.new do
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: "ruby-graphql", **rest)
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
- analytics_available = defined?(Datadog::Contrib::Analytics) \
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
- span.span_type = 'custom'
39
- if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
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
- # For top span of query, set the analytics sample rate tag, if available.
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.span_type = 'custom'
94
- if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
95
- span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
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.span_type = 'custom'
130
- if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
131
- span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
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.span_type = 'custom'
163
- if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
164
- span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
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: service_name) do |span|
19
- span.span_type = 'custom'
20
- if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
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
- # For top span of query, set the analytics sample rate tag, if available.
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
- analytics_available? && Datadog::Contrib::Analytics.enabled?(options.fetch(:analytics_enabled, false))
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
- schema_defn.tracer(tracer)
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
- class PrometheusTracing < PlatformTracing
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