graphql 2.0.14 → 2.0.32

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  3. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  4. data/lib/generators/graphql/relay.rb +18 -1
  5. data/lib/graphql/analysis/ast/visitor.rb +42 -35
  6. data/lib/graphql/analysis/ast.rb +2 -2
  7. data/lib/graphql/backtrace/table.rb +2 -2
  8. data/lib/graphql/backtrace/trace.rb +96 -0
  9. data/lib/graphql/backtrace/tracer.rb +1 -1
  10. data/lib/graphql/backtrace.rb +2 -1
  11. data/lib/graphql/dataloader/source.rb +69 -45
  12. data/lib/graphql/dataloader.rb +8 -5
  13. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  14. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  15. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  16. data/lib/graphql/execution/interpreter/runtime.rb +355 -268
  17. data/lib/graphql/execution/interpreter.rb +19 -15
  18. data/lib/graphql/execution/lazy.rb +6 -12
  19. data/lib/graphql/execution/lookahead.rb +16 -5
  20. data/lib/graphql/execution/multiplex.rb +2 -1
  21. data/lib/graphql/filter.rb +8 -2
  22. data/lib/graphql/introspection/directive_type.rb +2 -2
  23. data/lib/graphql/introspection/entry_points.rb +1 -1
  24. data/lib/graphql/introspection/field_type.rb +1 -1
  25. data/lib/graphql/introspection/schema_type.rb +2 -2
  26. data/lib/graphql/introspection/type_type.rb +5 -5
  27. data/lib/graphql/introspection.rb +1 -1
  28. data/lib/graphql/language/document_from_schema_definition.rb +58 -35
  29. data/lib/graphql/language/lexer.rb +248 -1505
  30. data/lib/graphql/language/nodes.rb +69 -40
  31. data/lib/graphql/language/parser.rb +775 -742
  32. data/lib/graphql/language/parser.y +44 -38
  33. data/lib/graphql/language/printer.rb +48 -25
  34. data/lib/graphql/language/visitor.rb +192 -81
  35. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  36. data/lib/graphql/pagination/connection.rb +5 -5
  37. data/lib/graphql/query/context.rb +93 -27
  38. data/lib/graphql/query/null_context.rb +8 -18
  39. data/lib/graphql/query/validation_pipeline.rb +2 -1
  40. data/lib/graphql/query.rb +55 -13
  41. data/lib/graphql/rake_task.rb +28 -1
  42. data/lib/graphql/schema/addition.rb +38 -12
  43. data/lib/graphql/schema/always_visible.rb +10 -0
  44. data/lib/graphql/schema/argument.rb +15 -23
  45. data/lib/graphql/schema/build_from_definition.rb +54 -25
  46. data/lib/graphql/schema/directive/transform.rb +1 -1
  47. data/lib/graphql/schema/directive.rb +12 -2
  48. data/lib/graphql/schema/enum.rb +24 -17
  49. data/lib/graphql/schema/enum_value.rb +3 -4
  50. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  51. data/lib/graphql/schema/field.rb +95 -73
  52. data/lib/graphql/schema/field_extension.rb +1 -4
  53. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  54. data/lib/graphql/schema/input_object.rb +9 -7
  55. data/lib/graphql/schema/interface.rb +5 -11
  56. data/lib/graphql/schema/introspection_system.rb +1 -1
  57. data/lib/graphql/schema/late_bound_type.rb +2 -0
  58. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -14
  59. data/lib/graphql/schema/member/build_type.rb +11 -3
  60. data/lib/graphql/schema/member/has_arguments.rb +114 -65
  61. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  62. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  63. data/lib/graphql/schema/member/has_directives.rb +81 -61
  64. data/lib/graphql/schema/member/has_fields.rb +95 -38
  65. data/lib/graphql/schema/member/has_interfaces.rb +49 -8
  66. data/lib/graphql/schema/member/has_validators.rb +32 -6
  67. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  68. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  69. data/lib/graphql/schema/object.rb +8 -5
  70. data/lib/graphql/schema/printer.rb +3 -1
  71. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  72. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  73. data/lib/graphql/schema/resolver.rb +16 -14
  74. data/lib/graphql/schema/timeout.rb +25 -29
  75. data/lib/graphql/schema/type_membership.rb +3 -0
  76. data/lib/graphql/schema/union.rb +10 -1
  77. data/lib/graphql/schema/validator.rb +2 -2
  78. data/lib/graphql/schema/warden.rb +64 -7
  79. data/lib/graphql/schema.rb +171 -28
  80. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  81. data/lib/graphql/static_validation/literal_validator.rb +15 -1
  82. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  83. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  84. data/lib/graphql/static_validation/validator.rb +1 -1
  85. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
  86. data/lib/graphql/subscriptions/event.rb +2 -7
  87. data/lib/graphql/subscriptions.rb +5 -0
  88. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  89. data/lib/graphql/tracing/appoptics_trace.rb +255 -0
  90. data/lib/graphql/tracing/appsignal_trace.rb +81 -0
  91. data/lib/graphql/tracing/data_dog_trace.rb +187 -0
  92. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  93. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  94. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  95. data/lib/graphql/tracing/notifications_trace.rb +49 -0
  96. data/lib/graphql/tracing/platform_trace.rb +123 -0
  97. data/lib/graphql/tracing/platform_tracing.rb +15 -3
  98. data/lib/graphql/tracing/prometheus_trace.rb +93 -0
  99. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  100. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  101. data/lib/graphql/tracing/scout_trace.rb +75 -0
  102. data/lib/graphql/tracing/statsd_trace.rb +60 -0
  103. data/lib/graphql/tracing/trace.rb +75 -0
  104. data/lib/graphql/tracing.rb +17 -39
  105. data/lib/graphql/type_kinds.rb +6 -3
  106. data/lib/graphql/types/relay/base_connection.rb +1 -1
  107. data/lib/graphql/types/relay/connection_behaviors.rb +28 -6
  108. data/lib/graphql/types/relay/edge_behaviors.rb +16 -5
  109. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  110. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  111. data/lib/graphql/types/relay.rb +0 -1
  112. data/lib/graphql/types/string.rb +1 -1
  113. data/lib/graphql/version.rb +1 -1
  114. data/lib/graphql.rb +16 -9
  115. data/readme.md +1 -1
  116. metadata +66 -29
  117. data/lib/graphql/language/lexer.rl +0 -280
  118. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+
6
+ # This class uses the AppopticsAPM SDK from the appoptics_apm gem to create
7
+ # traces for GraphQL.
8
+ #
9
+ # There are 4 configurations available. They can be set in the
10
+ # appoptics_apm config file or in code. Please see:
11
+ # {https://docs.appoptics.com/kb/apm_tracing/ruby/configure}
12
+ #
13
+ # AppOpticsAPM::Config[:graphql][:enabled] = true|false
14
+ # AppOpticsAPM::Config[:graphql][:transaction_name] = true|false
15
+ # AppOpticsAPM::Config[:graphql][:sanitize_query] = true|false
16
+ # AppOpticsAPM::Config[:graphql][:remove_comments] = true|false
17
+ module AppOpticsTrace
18
+ # These GraphQL events will show up as 'graphql.prep' spans
19
+ PREP_KEYS = ['lex', 'parse', 'validate', 'analyze_query', 'analyze_multiplex'].freeze
20
+ # These GraphQL events will show up as 'graphql.execute' spans
21
+ EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
22
+
23
+ # During auto-instrumentation this version of AppOpticsTracing is compared
24
+ # with the version provided in the appoptics_apm gem, so that the newer
25
+ # version of the class can be used
26
+
27
+ def self.version
28
+ Gem::Version.new('1.0.0')
29
+ end
30
+
31
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
32
+
33
+ [
34
+ 'lex',
35
+ 'parse',
36
+ 'validate',
37
+ 'analyze_query',
38
+ 'analyze_multiplex',
39
+ 'execute_multiplex',
40
+ 'execute_query',
41
+ 'execute_query_lazy',
42
+ ].each do |trace_method|
43
+ module_eval <<-RUBY, __FILE__, __LINE__
44
+ def #{trace_method}(**data)
45
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
46
+ layer = span_name("#{trace_method}")
47
+ kvs = metadata(data, layer)
48
+ kvs[:Key] = "#{trace_method}" if (PREP_KEYS + EXEC_KEYS).include?("#{trace_method}")
49
+
50
+ transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute'
51
+
52
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
53
+ kvs.clear # we don't have to send them twice
54
+ super
55
+ end
56
+ end
57
+ RUBY
58
+ end
59
+
60
+ # rubocop:enable Development/NoEvalCop
61
+
62
+ def execute_field(query:, field:, ast_node:, arguments:, object:)
63
+ return_type = field.type.unwrap
64
+ trace_field = if return_type.kind.scalar? || return_type.kind.enum?
65
+ (field.trace.nil? && @trace_scalars) || field.trace
66
+ else
67
+ true
68
+ end
69
+ platform_key = if trace_field
70
+ @platform_key_cache[AppOpticsTrace].platform_field_key_cache[field]
71
+ else
72
+ nil
73
+ end
74
+ if platform_key && trace_field
75
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
76
+ layer = platform_key
77
+ kvs = metadata({query: query, field: field, ast_node: ast_node, arguments: arguments, object: object}, layer)
78
+
79
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
80
+ kvs.clear # we don't have to send them twice
81
+ super
82
+ end
83
+ else
84
+ super
85
+ end
86
+ end
87
+
88
+ def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
89
+ execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
90
+ end
91
+
92
+ def authorized(**data)
93
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
94
+ layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
95
+ kvs = metadata(data, layer)
96
+
97
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
98
+ kvs.clear # we don't have to send them twice
99
+ super
100
+ end
101
+ end
102
+
103
+ def authorized_lazy(**data)
104
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
105
+ layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
106
+ kvs = metadata(data, layer)
107
+
108
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
109
+ kvs.clear # we don't have to send them twice
110
+ super
111
+ end
112
+ end
113
+
114
+ def resolve_type(**data)
115
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
116
+ layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
117
+
118
+ kvs = metadata(data, layer)
119
+
120
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
121
+ kvs.clear # we don't have to send them twice
122
+ super
123
+ end
124
+ end
125
+
126
+ def resolve_type_lazy(**data)
127
+ return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
128
+ layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
129
+ kvs = metadata(data, layer)
130
+
131
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
132
+ kvs.clear # we don't have to send them twice
133
+ super
134
+ end
135
+ end
136
+
137
+ include PlatformTrace
138
+
139
+ def platform_field_key(field)
140
+ "graphql.#{field.owner.graphql_name}.#{field.graphql_name}"
141
+ end
142
+
143
+ def platform_authorized_key(type)
144
+ "graphql.authorized.#{type.graphql_name}"
145
+ end
146
+
147
+ def platform_resolve_type_key(type)
148
+ "graphql.resolve_type.#{type.graphql_name}"
149
+ end
150
+
151
+ private
152
+
153
+ def gql_config
154
+ ::AppOpticsAPM::Config[:graphql] ||= {}
155
+ end
156
+
157
+ def transaction_name(query)
158
+ return if gql_config[:transaction_name] == false ||
159
+ ::AppOpticsAPM::SDK.get_transaction_name
160
+
161
+ split_query = query.strip.split(/\W+/, 3)
162
+ split_query[0] = 'query' if split_query[0].empty?
163
+ name = "graphql.#{split_query[0..1].join('.')}"
164
+
165
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
166
+ end
167
+
168
+ def multiplex_transaction_name(names)
169
+ return if gql_config[:transaction_name] == false ||
170
+ ::AppOpticsAPM::SDK.get_transaction_name
171
+
172
+ name = "graphql.multiplex.#{names.join('.')}"
173
+ name = "#{name[0..251]}..." if name.length > 254
174
+
175
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
176
+ end
177
+
178
+ def span_name(key)
179
+ return 'graphql.prep' if PREP_KEYS.include?(key)
180
+ return 'graphql.execute' if EXEC_KEYS.include?(key)
181
+
182
+ key[/^graphql\./] ? key : "graphql.#{key}"
183
+ end
184
+
185
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
186
+ def metadata(data, layer)
187
+ data.keys.map do |key|
188
+ case key
189
+ when :context
190
+ graphql_context(data[key], layer)
191
+ when :query
192
+ graphql_query(data[key])
193
+ when :query_string
194
+ graphql_query_string(data[key])
195
+ when :multiplex
196
+ graphql_multiplex(data[key])
197
+ when :path
198
+ [key, data[key].join(".")]
199
+ else
200
+ [key, data[key]]
201
+ end
202
+ end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
203
+ end
204
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
205
+
206
+ def graphql_context(context, layer)
207
+ context.errors && context.errors.each do |err|
208
+ AppOpticsAPM::API.log_exception(layer, err)
209
+ end
210
+
211
+ [[:Path, context.path.join('.')]]
212
+ end
213
+
214
+ def graphql_query(query)
215
+ return [] unless query
216
+
217
+ query_string = query.query_string
218
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
219
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
220
+
221
+ [[:InboundQuery, query_string],
222
+ [:Operation, query.selected_operation_name]]
223
+ end
224
+
225
+ def graphql_query_string(query_string)
226
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
227
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
228
+
229
+ [:InboundQuery, query_string]
230
+ end
231
+
232
+ def graphql_multiplex(data)
233
+ names = data.queries.map(&:operations).map(&:keys).flatten.compact
234
+ multiplex_transaction_name(names) if names.size > 1
235
+
236
+ [:Operations, names.join(', ')]
237
+ end
238
+
239
+ def sanitize(query)
240
+ return unless query
241
+
242
+ # remove arguments
243
+ query.gsub(/"[^"]*"/, '"?"') # strings
244
+ .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
245
+ .gsub(/\[[^\]]*\]/, '[?]') # arrays
246
+ end
247
+
248
+ def remove_comments(query)
249
+ return unless query
250
+
251
+ query.gsub(/#[^\n\r]*/, '')
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module AppsignalTrace
6
+ include PlatformTrace
7
+
8
+ # @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
9
+ # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
10
+ # It can also be specified per-query with `context[:set_appsignal_action_name]`.
11
+ def initialize(set_action_name: false, **rest)
12
+ @set_action_name = set_action_name
13
+ super
14
+ end
15
+
16
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
+
18
+ {
19
+ "lex" => "lex.graphql",
20
+ "parse" => "parse.graphql",
21
+ "validate" => "validate.graphql",
22
+ "analyze_query" => "analyze.graphql",
23
+ "analyze_multiplex" => "analyze.graphql",
24
+ "execute_multiplex" => "execute.graphql",
25
+ "execute_query" => "execute.graphql",
26
+ "execute_query_lazy" => "execute.graphql",
27
+ }.each do |trace_method, platform_key|
28
+ module_eval <<-RUBY, __FILE__, __LINE__
29
+ def #{trace_method}(**data)
30
+ #{
31
+ if trace_method == "execute_query"
32
+ <<-RUBY
33
+ set_this_txn_name = data[:query].context[:set_appsignal_action_name]
34
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
35
+ Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
36
+ end
37
+ RUBY
38
+ end
39
+ }
40
+
41
+ Appsignal.instrument("#{platform_key}") do
42
+ super
43
+ end
44
+ end
45
+ RUBY
46
+ end
47
+
48
+ # rubocop:enable Development/NoEvalCop
49
+
50
+ def platform_execute_field(platform_key)
51
+ Appsignal.instrument(platform_key) do
52
+ yield
53
+ end
54
+ end
55
+
56
+ def platform_authorized(platform_key)
57
+ Appsignal.instrument(platform_key) do
58
+ yield
59
+ end
60
+ end
61
+
62
+ def platform_resolve_type(platform_key)
63
+ Appsignal.instrument(platform_key) do
64
+ yield
65
+ end
66
+ end
67
+
68
+ def platform_field_key(field)
69
+ "#{field.owner.graphql_name}.#{field.graphql_name}.graphql"
70
+ end
71
+
72
+ def platform_authorized_key(type)
73
+ "#{type.graphql_name}.authorized.graphql"
74
+ end
75
+
76
+ def platform_resolve_type_key(type)
77
+ "#{type.graphql_name}.resolve_type.graphql"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module DataDogTrace
6
+ # @param tracer [#trace] Deprecated
7
+ # @param analytics_enabled [Boolean] Deprecated
8
+ # @param analytics_sample_rate [Float] Deprecated
9
+ def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **rest)
10
+ if tracer.nil?
11
+ tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
12
+ end
13
+ @tracer = tracer
14
+
15
+ @analytics_enabled = analytics_enabled
16
+ @analytics_sample_rate = analytics_sample_rate
17
+
18
+ @service_name = service
19
+ @has_prepare_span = respond_to?(:prepare_span)
20
+ super
21
+ end
22
+
23
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
24
+
25
+ {
26
+ 'lex' => 'lex.graphql',
27
+ 'parse' => 'parse.graphql',
28
+ 'validate' => 'validate.graphql',
29
+ 'analyze_query' => 'analyze.graphql',
30
+ 'analyze_multiplex' => 'analyze.graphql',
31
+ 'execute_multiplex' => 'execute.graphql',
32
+ 'execute_query' => 'execute.graphql',
33
+ 'execute_query_lazy' => 'execute.graphql',
34
+ }.each do |trace_method, trace_key|
35
+ module_eval <<-RUBY, __FILE__, __LINE__
36
+ def #{trace_method}(**data)
37
+ @tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span|
38
+ span.set_tag('component', 'graphql')
39
+ span.set_tag('operation', '#{trace_method}')
40
+
41
+ #{
42
+ if trace_method == 'execute_multiplex'
43
+ <<-RUBY
44
+ operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
45
+
46
+ resource = if operations.empty?
47
+ first_query = data[:multiplex].queries.first
48
+ fallback_transaction_name(first_query && first_query.context)
49
+ else
50
+ operations
51
+ end
52
+ span.resource = resource if resource
53
+
54
+ # [Deprecated] will be removed in the future
55
+ span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled
56
+ RUBY
57
+ elsif trace_method == 'execute_query'
58
+ <<-RUBY
59
+ span.set_tag(:selected_operation_name, data[:query].selected_operation_name)
60
+ span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
61
+ span.set_tag(:query_string, data[:query].query_string)
62
+ RUBY
63
+ end
64
+ }
65
+ if @has_prepare_span
66
+ prepare_span("#{trace_method.sub("platform_", "")}", data, span)
67
+ end
68
+ super
69
+ end
70
+ end
71
+ RUBY
72
+ end
73
+
74
+ # rubocop:enable Development/NoEvalCop
75
+
76
+ def execute_field_span(span_key, query, field, ast_node, arguments, object)
77
+ return_type = field.type.unwrap
78
+ trace_field = if return_type.kind.scalar? || return_type.kind.enum?
79
+ (field.trace.nil? && @trace_scalars) || field.trace
80
+ else
81
+ true
82
+ end
83
+ platform_key = if trace_field
84
+ @platform_key_cache[DataDogTrace].platform_field_key_cache[field]
85
+ else
86
+ nil
87
+ end
88
+ if platform_key && trace_field
89
+ @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
90
+ span.set_tag('component', 'graphql')
91
+ span.set_tag('operation', span_key)
92
+
93
+ if @has_prepare_span
94
+ prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
95
+ prepare_span(span_key, prepare_span_data, span)
96
+ end
97
+ yield
98
+ end
99
+ else
100
+ yield
101
+ end
102
+ end
103
+ def execute_field(query:, field:, ast_node:, arguments:, object:)
104
+ execute_field_span("execute_field", query, field, ast_node, arguments, object) do
105
+ super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
106
+ end
107
+ end
108
+
109
+ def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
110
+ execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do
111
+ super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
112
+ end
113
+ end
114
+
115
+ def authorized(query:, type:, object:)
116
+ authorized_span("authorized", object, type, query) do
117
+ super(query: query, type: type, object: object)
118
+ end
119
+ end
120
+
121
+ def authorized_span(span_key, object, type, query)
122
+ platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
123
+ @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
124
+ span.set_tag('component', 'graphql')
125
+ span.set_tag('operation', span_key)
126
+
127
+ if @has_prepare_span
128
+ prepare_span(span_key, {object: object, type: type, query: query}, span)
129
+ end
130
+ yield
131
+ end
132
+ end
133
+
134
+ def authorized_lazy(object:, type:, query:)
135
+ authorized_span("authorized_lazy", object, type, query) do
136
+ super(query: query, type: type, object: object)
137
+ end
138
+ end
139
+
140
+ def resolve_type(object:, type:, query:)
141
+ resolve_type_span("resolve_type", object, type, query) do
142
+ super(object: object, query: query, type: type)
143
+ end
144
+ end
145
+
146
+ def resolve_type_lazy(object:, type:, query:)
147
+ resolve_type_span("resolve_type_lazy", object, type, query) do
148
+ super(object: object, query: query, type: type)
149
+ end
150
+ end
151
+
152
+ def resolve_type_span(span_key, object, type, query)
153
+ platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
154
+ @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
155
+ span.set_tag('component', 'graphql')
156
+ span.set_tag('operation', span_key)
157
+
158
+ if @has_prepare_span
159
+ prepare_span(span_key, {object: object, type: type, query: query}, span)
160
+ end
161
+ yield
162
+ end
163
+ end
164
+
165
+ include PlatformTrace
166
+
167
+ # Implement this method in a subclass to apply custom tags to datadog spans
168
+ # @param key [String] The event being traced
169
+ # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
170
+ # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
171
+ # def prepare_span(key, data, span)
172
+ # end
173
+
174
+ def platform_field_key(field)
175
+ field.path
176
+ end
177
+
178
+ def platform_authorized_key(type)
179
+ "#{type.graphql_name}.authorized"
180
+ end
181
+
182
+ def platform_resolve_type_key(type)
183
+ "#{type.graphql_name}.resolve_type"
184
+ end
185
+ end
186
+ end
187
+ 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,69 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Tracing
4
+ # This trace class calls legacy-style tracer with payload hashes.
5
+ # New-style `trace_with` modules significantly reduce the overhead of tracing,
6
+ # but that advantage is lost when legacy-style tracers are also used (since the payload hashes are still constructed).
7
+ module CallLegacyTracers
8
+ def lex(query_string:)
9
+ (@multiplex || @query).trace("lex", { query_string: query_string }) { super }
10
+ end
11
+
12
+ def parse(query_string:)
13
+ (@multiplex || @query).trace("parse", { query_string: query_string }) { super }
14
+ end
15
+
16
+ def validate(query:, validate:)
17
+ query.trace("validate", { validate: validate, query: query }) { super }
18
+ end
19
+
20
+ def analyze_multiplex(multiplex:)
21
+ multiplex.trace("analyze_multiplex", { multiplex: multiplex }) { super }
22
+ end
23
+
24
+ def analyze_query(query:)
25
+ query.trace("analyze_query", { query: query }) { super }
26
+ end
27
+
28
+ def execute_multiplex(multiplex:)
29
+ multiplex.trace("execute_multiplex", { multiplex: multiplex }) { super }
30
+ end
31
+
32
+ def execute_query(query:)
33
+ query.trace("execute_query", { query: query }) { super }
34
+ end
35
+
36
+ def execute_query_lazy(query:, multiplex:)
37
+ multiplex.trace("execute_query_lazy", { multiplex: multiplex, query: query }) { super }
38
+ end
39
+
40
+ def execute_field(field:, query:, ast_node:, arguments:, object:)
41
+ query.trace("execute_field", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
42
+ end
43
+
44
+ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
45
+ query.trace("execute_field_lazy", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
46
+ end
47
+
48
+ def authorized(query:, type:, object:)
49
+ query.trace("authorized", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
50
+ end
51
+
52
+ def authorized_lazy(query:, type:, object:)
53
+ query.trace("authorized_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
54
+ end
55
+
56
+ def resolve_type(query:, type:, object:)
57
+ query.trace("resolve_type", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
58
+ end
59
+
60
+ def resolve_type_lazy(query:, type:, object:)
61
+ query.trace("resolve_type_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
62
+ end
63
+ end
64
+
65
+ class LegacyTrace < Trace
66
+ include CallLegacyTracers
67
+ end
68
+ end
69
+ end