graphql 2.0.16 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/visitor.rb +42 -35
  3. data/lib/graphql/analysis/ast.rb +2 -2
  4. data/lib/graphql/backtrace/trace.rb +96 -0
  5. data/lib/graphql/backtrace/tracer.rb +1 -1
  6. data/lib/graphql/backtrace.rb +6 -1
  7. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  8. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -3
  9. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  10. data/lib/graphql/execution/interpreter/runtime.rb +264 -211
  11. data/lib/graphql/execution/interpreter.rb +15 -10
  12. data/lib/graphql/execution/lazy.rb +6 -12
  13. data/lib/graphql/execution/multiplex.rb +2 -1
  14. data/lib/graphql/filter.rb +7 -2
  15. data/lib/graphql/introspection/directive_type.rb +2 -2
  16. data/lib/graphql/introspection/field_type.rb +1 -1
  17. data/lib/graphql/introspection/schema_type.rb +2 -2
  18. data/lib/graphql/introspection/type_type.rb +5 -5
  19. data/lib/graphql/language/document_from_schema_definition.rb +25 -9
  20. data/lib/graphql/language/lexer.rb +216 -1505
  21. data/lib/graphql/language/nodes.rb +66 -40
  22. data/lib/graphql/language/parser.rb +509 -491
  23. data/lib/graphql/language/parser.y +43 -38
  24. data/lib/graphql/language/visitor.rb +191 -83
  25. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  26. data/lib/graphql/pagination/connection.rb +5 -5
  27. data/lib/graphql/query/context.rb +62 -31
  28. data/lib/graphql/query/null_context.rb +1 -1
  29. data/lib/graphql/query.rb +22 -5
  30. data/lib/graphql/schema/argument.rb +7 -13
  31. data/lib/graphql/schema/build_from_definition.rb +15 -3
  32. data/lib/graphql/schema/directive.rb +12 -2
  33. data/lib/graphql/schema/enum.rb +24 -17
  34. data/lib/graphql/schema/enum_value.rb +2 -3
  35. data/lib/graphql/schema/field.rb +68 -57
  36. data/lib/graphql/schema/field_extension.rb +1 -4
  37. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  38. data/lib/graphql/schema/interface.rb +0 -10
  39. data/lib/graphql/schema/late_bound_type.rb +2 -0
  40. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -14
  41. data/lib/graphql/schema/member/has_arguments.rb +105 -58
  42. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  43. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  44. data/lib/graphql/schema/member/has_directives.rb +15 -10
  45. data/lib/graphql/schema/member/has_fields.rb +95 -38
  46. data/lib/graphql/schema/member/has_interfaces.rb +49 -8
  47. data/lib/graphql/schema/member/has_validators.rb +32 -6
  48. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  49. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  50. data/lib/graphql/schema/object.rb +2 -4
  51. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  52. data/lib/graphql/schema/resolver.rb +4 -4
  53. data/lib/graphql/schema/timeout.rb +24 -28
  54. data/lib/graphql/schema/validator.rb +1 -1
  55. data/lib/graphql/schema/warden.rb +29 -5
  56. data/lib/graphql/schema.rb +76 -25
  57. data/lib/graphql/static_validation/literal_validator.rb +15 -1
  58. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  59. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  60. data/lib/graphql/static_validation/validator.rb +1 -1
  61. data/lib/graphql/subscriptions/event.rb +2 -7
  62. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  63. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  64. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  65. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  66. data/lib/graphql/tracing/legacy_trace.rb +65 -0
  67. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  68. data/lib/graphql/tracing/notifications_trace.rb +42 -0
  69. data/lib/graphql/tracing/platform_trace.rb +109 -0
  70. data/lib/graphql/tracing/platform_tracing.rb +15 -3
  71. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  72. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  73. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  74. data/lib/graphql/tracing/scout_trace.rb +72 -0
  75. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  76. data/lib/graphql/tracing/trace.rb +75 -0
  77. data/lib/graphql/tracing.rb +16 -39
  78. data/lib/graphql/type_kinds.rb +6 -3
  79. data/lib/graphql/types/relay/base_connection.rb +1 -1
  80. data/lib/graphql/types/relay/connection_behaviors.rb +24 -6
  81. data/lib/graphql/types/relay/edge_behaviors.rb +16 -6
  82. data/lib/graphql/types/relay/node_behaviors.rb +7 -1
  83. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  84. data/lib/graphql/types/relay.rb +0 -1
  85. data/lib/graphql/types/string.rb +1 -1
  86. data/lib/graphql/version.rb +1 -1
  87. data/lib/graphql.rb +16 -9
  88. metadata +34 -9
  89. data/lib/graphql/language/lexer.rl +0 -280
  90. data/lib/graphql/types/relay/default_relay.rb +0 -27
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module DataDogTrace
6
+ # @param analytics_enabled [Boolean] Deprecated
7
+ # @param analytics_sample_rate [Float] Deprecated
8
+ def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: "ruby-graphql", **rest)
9
+ if tracer.nil?
10
+ tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
11
+ end
12
+ @tracer = tracer
13
+
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)
19
+ @analytics_sample_rate = analytics_sample_rate
20
+ @service_name = service
21
+ super
22
+ end
23
+
24
+ {
25
+ 'lex' => 'lex.graphql',
26
+ 'parse' => 'parse.graphql',
27
+ 'validate' => 'validate.graphql',
28
+ 'analyze_query' => 'analyze.graphql',
29
+ 'analyze_multiplex' => 'analyze.graphql',
30
+ 'execute_multiplex' => 'execute.graphql',
31
+ 'execute_query' => 'execute.graphql',
32
+ 'execute_query_lazy' => 'execute.graphql',
33
+ }.each do |trace_method, trace_key|
34
+ module_eval <<-RUBY, __FILE__, __LINE__
35
+ def #{trace_method}(**data)
36
+ @tracer.trace("#{trace_key}", service: @service_name) do |span|
37
+ span.span_type = 'custom'
38
+ if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
39
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
40
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, '#{trace_method}')
41
+ end
42
+
43
+ #{
44
+ if trace_method == 'execute_multiplex'
45
+ <<-RUBY
46
+ operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
47
+
48
+ resource = if operations.empty?
49
+ first_query = data[:multiplex].queries.first
50
+ fallback_transaction_name(first_query && first_query.context)
51
+ else
52
+ operations
53
+ end
54
+ span.resource = resource if resource
55
+
56
+ # For top span of query, set the analytics sample rate tag, if available.
57
+ if @analytics_enabled
58
+ Datadog::Contrib::Analytics.set_sample_rate(span, @analytics_sample_rate)
59
+ end
60
+ RUBY
61
+ elsif trace_method == 'execute_query'
62
+ <<-RUBY
63
+ span.set_tag(:selected_operation_name, data[:query].selected_operation_name)
64
+ span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
65
+ span.set_tag(:query_string, data[:query].query_string)
66
+ RUBY
67
+ end
68
+ }
69
+ prepare_span("#{trace_method.sub("platform_", "")}", data, span)
70
+ super
71
+ end
72
+ end
73
+ RUBY
74
+ end
75
+
76
+ def platform_execute_field(platform_key, data, span_key = "execute_field")
77
+ @tracer.trace(platform_key, service: @service_name) do |span|
78
+ span.span_type = 'custom'
79
+ if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
80
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
81
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
82
+ end
83
+ prepare_span(span_key, data, span)
84
+ yield
85
+ end
86
+ end
87
+
88
+ def platform_execute_field_lazy(platform_key, data, &block)
89
+ platform_execute_field(platform_key, data, "execute_field_lazy", &block)
90
+ end
91
+
92
+ def authorized(object:, type:, query:, span_key: "authorized")
93
+ platform_key = @platform_authorized_key_cache[type]
94
+ @tracer.trace(platform_key, service: @service_name) do |span|
95
+ span.span_type = 'custom'
96
+ if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
97
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
98
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
99
+ end
100
+ prepare_span(span_key, {object: object, type: type, query: query}, span)
101
+ super(query: query, type: type, object: object)
102
+ end
103
+ end
104
+
105
+ def authorized_lazy(**kwargs, &block)
106
+ authorized(span_key: "authorized_lazy", **kwargs, &block)
107
+ end
108
+
109
+ def resolve_type(object:, type:, query:, span_key: "resolve_type")
110
+ platform_key = @platform_resolve_type_key_cache[type]
111
+ @tracer.trace(platform_key, service: @service_name) do |span|
112
+ span.span_type = 'custom'
113
+ if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
114
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
115
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
116
+ end
117
+ prepare_span(span_key, {object: object, type: type, query: query}, span)
118
+ super(query: query, type: type, object: object)
119
+ end
120
+ end
121
+
122
+ def resolve_type_lazy(**kwargs, &block)
123
+ resolve_type(span_key: "resolve_type_lazy", **kwargs, &block)
124
+ end
125
+
126
+ include PlatformTrace
127
+
128
+ # Implement this method in a subclass to apply custom tags to datadog spans
129
+ # @param key [String] The event being traced
130
+ # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
131
+ # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
132
+ def prepare_span(key, data, span)
133
+ end
134
+
135
+ def platform_field_key(field)
136
+ field.path
137
+ end
138
+
139
+ def platform_authorized_key(type)
140
+ "#{type.graphql_name}.authorized"
141
+ end
142
+
143
+ def platform_resolve_type_key(type)
144
+ "#{type.graphql_name}.resolve_type"
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,65 @@
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
+ class LegacyTrace < Trace
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
+ end
65
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module NewRelicTrace
6
+ include PlatformTrace
7
+
8
+ # @param set_transaction_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_new_relic_transaction_name]`.
11
+ def initialize(set_transaction_name: false, **_rest)
12
+ @set_transaction_name = set_transaction_name
13
+ super
14
+ end
15
+
16
+ def execute_query(query:)
17
+ set_this_txn_name = query.context[:set_new_relic_transaction_name]
18
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
19
+ NewRelic::Agent.set_transaction_name(transaction_name(query))
20
+ end
21
+ NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped("GraphQL/execute") do
22
+ super
23
+ end
24
+ end
25
+
26
+ {
27
+ "lex" => "GraphQL/lex",
28
+ "parse" => "GraphQL/parse",
29
+ "validate" => "GraphQL/validate",
30
+ "analyze_query" => "GraphQL/analyze",
31
+ "analyze_multiplex" => "GraphQL/analyze",
32
+ "execute_multiplex" => "GraphQL/execute",
33
+ "execute_query_lazy" => "GraphQL/execute",
34
+ }.each do |trace_method, platform_key|
35
+ module_eval <<-RUBY, __FILE__, __LINE__
36
+ def #{trace_method}(**_keys)
37
+ NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped("#{platform_key}") do
38
+ super
39
+ end
40
+ end
41
+ RUBY
42
+ end
43
+
44
+ def platform_execute_field(platform_key)
45
+ NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(platform_key) do
46
+ yield
47
+ end
48
+ end
49
+
50
+ def platform_authorized(platform_key)
51
+ NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(platform_key) do
52
+ yield
53
+ end
54
+ end
55
+
56
+ def platform_resolve_type(platform_key)
57
+ NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(platform_key) do
58
+ yield
59
+ end
60
+ end
61
+
62
+ def platform_field_key(field)
63
+ "GraphQL/#{field.owner.graphql_name}/#{field.graphql_name}"
64
+ end
65
+
66
+ def platform_authorized_key(type)
67
+ "GraphQL/Authorize/#{type.graphql_name}"
68
+ end
69
+
70
+ def platform_resolve_type_key(type)
71
+ "GraphQL/ResolveType/#{type.graphql_name}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ # This implementation forwards events to a notification handler (i.e.
6
+ # ActiveSupport::Notifications or Dry::Monitor::Notifications)
7
+ # with a `graphql` suffix.
8
+ module NotificationsTrace
9
+ # Initialize a new NotificationsTracing instance
10
+ #
11
+ # @param engine [#instrument(key, metadata, block)] The notifications engine to use
12
+ def initialize(engine:, **rest)
13
+ @notifications_engine = engine
14
+ super
15
+ end
16
+
17
+ {
18
+ "lex" => "lex.graphql",
19
+ "parse" => "parse.graphql",
20
+ "validate" => "validate.graphql",
21
+ "analyze_multiplex" => "analyze_multiplex.graphql",
22
+ "analyze_query" => "analyze_query.graphql",
23
+ "execute_query" => "execute_query.graphql",
24
+ "execute_query_lazy" => "execute_query_lazy.graphql",
25
+ "execute_field" => "execute_field.graphql",
26
+ "execute_field_lazy" => "execute_field_lazy.graphql",
27
+ "authorized" => "authorized.graphql",
28
+ "authorized_lazy" => "authorized_lazy.graphql",
29
+ "resolve_type" => "resolve_type.graphql",
30
+ "resolve_type_lazy" => "resolve_type.graphql",
31
+ }.each do |trace_method, platform_key|
32
+ module_eval <<-RUBY, __FILE__, __LINE__
33
+ def #{trace_method}(**metadata, &blk)
34
+ @notifications_engine.instrument("#{platform_key}", metadata, &blk)
35
+ end
36
+ RUBY
37
+ end
38
+
39
+ include PlatformTrace
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module PlatformTrace
6
+ def initialize(trace_scalars: false, **_options)
7
+ @trace_scalars = trace_scalars
8
+ @platform_field_key_cache = Hash.new { |h, k| h[k] = platform_field_key(k) }
9
+ @platform_authorized_key_cache = Hash.new { |h, k| h[k] = platform_authorized_key(k) }
10
+ @platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) }
11
+ super
12
+ end
13
+
14
+ def platform_execute_field_lazy(*args, &block)
15
+ platform_execute_field(*args, &block)
16
+ end
17
+
18
+ def platform_authorized_lazy(key, &block)
19
+ platform_authorized(key, &block)
20
+ end
21
+
22
+ def platform_resolve_type_lazy(key, &block)
23
+ platform_resolve_type(key, &block)
24
+ end
25
+
26
+ def self.included(child_class)
27
+ # Don't gather this unless necessary
28
+ pass_data_to_execute_field = child_class.method_defined?(:platform_execute_field) &&
29
+ child_class.instance_method(:platform_execute_field).arity != 1
30
+
31
+ [:execute_field, :execute_field_lazy].each do |field_trace_method|
32
+ if !child_class.method_defined?(field_trace_method)
33
+ child_class.module_eval <<-RUBY, __FILE__, __LINE__
34
+ def #{field_trace_method}(query:, field:, ast_node:, arguments:, object:)
35
+ return_type = field.type.unwrap
36
+ trace_field = if return_type.kind.scalar? || return_type.kind.enum?
37
+ (field.trace.nil? && @trace_scalars) || field.trace
38
+ else
39
+ true
40
+ end
41
+ platform_key = if trace_field
42
+ @platform_field_key_cache[field]
43
+ else
44
+ nil
45
+ end
46
+ if platform_key && trace_field
47
+ platform_#{field_trace_method}(platform_key#{pass_data_to_execute_field ? ", { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }" : ""}) do
48
+ super
49
+ end
50
+ else
51
+ super
52
+ end
53
+ end
54
+ RUBY
55
+ end
56
+ end
57
+
58
+
59
+ [:authorized, :authorized_lazy].each do |auth_trace_method|
60
+ if !child_class.method_defined?(auth_trace_method)
61
+ child_class.module_eval <<-RUBY, __FILE__, __LINE__
62
+ def #{auth_trace_method}(type:, query:, object:)
63
+ platform_key = @platform_authorized_key_cache[type]
64
+ platform_#{auth_trace_method}(platform_key) do
65
+ super
66
+ end
67
+ end
68
+ RUBY
69
+ end
70
+ end
71
+
72
+ [:resolve_type, :resolve_type_lazy].each do |rt_trace_method|
73
+ if !child_class.method_defined?(rt_trace_method)
74
+ child_class.module_eval <<-RUBY, __FILE__, __LINE__
75
+ def #{rt_trace_method}(query:, type:, object:)
76
+ platform_key = @platform_resolve_type_key_cache[type]
77
+ platform_#{rt_trace_method}(platform_key) do
78
+ super
79
+ end
80
+ end
81
+ RUBY
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+
88
+ private
89
+
90
+ # Get the transaction name based on the operation type and name if possible, or fall back to a user provided
91
+ # one. Useful for anonymous queries.
92
+ def transaction_name(query)
93
+ selected_op = query.selected_operation
94
+ txn_name = if selected_op
95
+ op_type = selected_op.operation_type
96
+ op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous"
97
+ "#{op_type}.#{op_name}"
98
+ else
99
+ "query.anonymous"
100
+ end
101
+ "GraphQL/#{txn_name}"
102
+ end
103
+
104
+ def fallback_transaction_name(context)
105
+ context[:tracing_fallback_transaction_name]
106
+ end
107
+ end
108
+ end
109
+ end
@@ -40,7 +40,7 @@ module GraphQL
40
40
 
41
41
  platform_key = if trace_field
42
42
  context = data.fetch(:query).context
43
- cached_platform_key(context, field, :field) { platform_field_key(data[:owner], field) }
43
+ cached_platform_key(context, field, :field) { platform_field_key(field.owner, field) }
44
44
  else
45
45
  nil
46
46
  end
@@ -73,8 +73,20 @@ module GraphQL
73
73
  end
74
74
 
75
75
  def self.use(schema_defn, options = {})
76
- tracer = self.new(**options)
77
- schema_defn.tracer(tracer)
76
+ if options[:legacy_tracing]
77
+ tracer = self.new(**options)
78
+ schema_defn.tracer(tracer)
79
+ else
80
+ tracing_name = self.name.split("::").last
81
+ trace_name = tracing_name.sub("Tracing", "Trace")
82
+ if GraphQL::Tracing.const_defined?(trace_name, false)
83
+ trace_module = GraphQL::Tracing.const_get(trace_name)
84
+ schema_defn.trace_with(trace_module, **options)
85
+ else
86
+ tracer = self.new(**options)
87
+ schema_defn.tracer(tracer)
88
+ end
89
+ end
78
90
  end
79
91
 
80
92
  private
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module PrometheusTrace
6
+ include PlatformTrace
7
+
8
+ def initialize(client: PrometheusExporter::Client.default, keys_whitelist: ["execute_field", "execute_field_lazy"], collector_type: "graphql", **rest)
9
+ @client = client
10
+ @keys_whitelist = keys_whitelist
11
+ @collector_type = collector_type
12
+
13
+ super(**rest)
14
+ end
15
+
16
+ {
17
+ 'lex' => "graphql.lex",
18
+ 'parse' => "graphql.parse",
19
+ 'validate' => "graphql.validate",
20
+ 'analyze_query' => "graphql.analyze",
21
+ 'analyze_multiplex' => "graphql.analyze",
22
+ 'execute_multiplex' => "graphql.execute",
23
+ 'execute_query' => "graphql.execute",
24
+ 'execute_query_lazy' => "graphql.execute",
25
+ }.each do |trace_method, platform_key|
26
+ module_eval <<-RUBY, __FILE__, __LINE__
27
+ def #{trace_method}(**data, &block)
28
+ instrument_execution("#{platform_key}", "#{trace_method}", &block)
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ def platform_execute_field(platform_key, &block)
34
+ instrument_execution(platform_key, "execute_field", &block)
35
+ end
36
+
37
+ def platform_execute_field_lazy(platform_key, &block)
38
+ instrument_execution(platform_key, "execute_field_lazy", &block)
39
+ end
40
+
41
+ def platform_authorized(platform_key, &block)
42
+ instrument_execution(platform_key, "authorized", &block)
43
+ end
44
+
45
+ def platform_authorized_lazy(platform_key, &block)
46
+ instrument_execution(platform_key, "authorized_lazy", &block)
47
+ end
48
+
49
+ def platform_resolve_type(platform_key, &block)
50
+ instrument_execution(platform_key, "resolve_type", &block)
51
+ end
52
+
53
+ def platform_resolve_type_lazy(platform_key, &block)
54
+ instrument_execution(platform_key, "resolve_type_lazy", &block)
55
+ end
56
+
57
+ def platform_field_key(field)
58
+ field.path
59
+ end
60
+
61
+ def platform_authorized_key(type)
62
+ "#{type.graphql_name}.authorized"
63
+ end
64
+
65
+ def platform_resolve_type_key(type)
66
+ "#{type.graphql_name}.resolve_type"
67
+ end
68
+
69
+ private
70
+
71
+ def instrument_execution(platform_key, key, &block)
72
+ if @keys_whitelist.include?(key)
73
+ start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
74
+ result = block.call
75
+ duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
76
+ @client.send_json(
77
+ type: @collector_type,
78
+ duration: duration,
79
+ platform_key: platform_key,
80
+ key: key
81
+ )
82
+ result
83
+ else
84
+ yield
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -5,7 +5,7 @@ module GraphQL
5
5
  class PrometheusTracing < PlatformTracing
6
6
  class GraphQLCollector < ::PrometheusExporter::Server::TypeCollector
7
7
  def initialize
8
- @graphql_gauge = PrometheusExporter::Metric::Summary.new(
8
+ @graphql_gauge = PrometheusExporter::Metric::Base.default_aggregation.new(
9
9
  'graphql_duration_seconds',
10
10
  'Time spent in GraphQL operations, in seconds'
11
11
  )
@@ -27,9 +27,9 @@ module GraphQL
27
27
  super opts
28
28
  end
29
29
 
30
- def platform_trace(platform_key, key, data, &block)
30
+ def platform_trace(platform_key, key, _data, &block)
31
31
  return yield unless @keys_whitelist.include?(key)
32
- instrument_execution(platform_key, key, data, &block)
32
+ instrument_execution(platform_key, key, &block)
33
33
  end
34
34
 
35
35
  def platform_field_key(type, field)
@@ -46,7 +46,7 @@ module GraphQL
46
46
 
47
47
  private
48
48
 
49
- def instrument_execution(platform_key, key, data, &block)
49
+ def instrument_execution(platform_key, key, &block)
50
50
  start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
51
51
  result = block.call
52
52
  duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module ScoutTrace
6
+ include PlatformTrace
7
+
8
+ INSTRUMENT_OPTS = { scope: true }
9
+
10
+ # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
11
+ # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
12
+ # It can also be specified per-query with `context[:set_scout_transaction_name]`.
13
+ def initialize(set_transaction_name: false, **_rest)
14
+ self.class.include(ScoutApm::Tracer)
15
+ @set_transaction_name = set_transaction_name
16
+ super
17
+ end
18
+
19
+ {
20
+ "lex" => "lex.graphql",
21
+ "parse" => "parse.graphql",
22
+ "validate" => "validate.graphql",
23
+ "analyze_query" => "analyze.graphql",
24
+ "analyze_multiplex" => "analyze.graphql",
25
+ "execute_multiplex" => "execute.graphql",
26
+ "execute_query" => "execute.graphql",
27
+ "execute_query_lazy" => "execute.graphql",
28
+ }.each do |trace_method, platform_key|
29
+ module_eval <<-RUBY, __FILE__, __LINE__
30
+ def #{trace_method}(**data)
31
+ #{
32
+ if trace_method == "execute_query"
33
+ <<-RUBY
34
+ set_this_txn_name = data[:query].context[:set_scout_transaction_name]
35
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
36
+ ScoutApm::Transaction.rename(transaction_name(data[:query]))
37
+ end
38
+ RUBY
39
+ end
40
+ }
41
+
42
+ self.class.instrument("GraphQL", "#{platform_key}", INSTRUMENT_OPTS) do
43
+ super
44
+ end
45
+ end
46
+ RUBY
47
+ end
48
+
49
+ def platform_execute_field(platform_key, &block)
50
+ self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
51
+ end
52
+
53
+ def platform_authorized(platform_key, &block)
54
+ self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
55
+ end
56
+
57
+ alias :platform_resolve_type :platform_authorized
58
+
59
+ def platform_field_key(field)
60
+ field.path
61
+ end
62
+
63
+ def platform_authorized_key(type)
64
+ "#{type.graphql_name}.authorized"
65
+ end
66
+
67
+ def platform_resolve_type_key(type)
68
+ "#{type.graphql_name}.resolve_type"
69
+ end
70
+ end
71
+ end
72
+ end