graphql 2.0.13 → 2.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/generators/graphql/relay.rb +18 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +8 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/backtrace/trace.rb +93 -0
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/backtrace.rb +2 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +89 -45
- data/lib/graphql/dataloader.rb +115 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +331 -455
- data/lib/graphql/execution/interpreter.rb +125 -61
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/lookahead.rb +124 -46
- data/lib/graphql/execution/multiplex.rb +3 -117
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/introspection/directive_type.rb +3 -3
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +10 -13
- data/lib/graphql/introspection/type_type.rb +17 -10
- data/lib/graphql/introspection.rb +3 -2
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +75 -59
- data/lib/graphql/language/lexer.rb +358 -1506
- data/lib/graphql/language/nodes.rb +166 -93
- data/lib/graphql/language/parser.rb +795 -1953
- data/lib/graphql/language/printer.rb +340 -160
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +188 -141
- data/lib/graphql/language.rb +61 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +33 -6
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +117 -112
- data/lib/graphql/query/null_context.rb +12 -25
- data/lib/graphql/query/validation_pipeline.rb +6 -5
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +86 -30
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +29 -11
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +59 -23
- data/lib/graphql/schema/always_visible.rb +11 -0
- data/lib/graphql/schema/argument.rb +55 -26
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +56 -32
- data/lib/graphql/schema/directive/one_of.rb +24 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +15 -3
- data/lib/graphql/schema/enum.rb +35 -24
- data/lib/graphql/schema/enum_value.rb +2 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +147 -107
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/has_single_input_argument.rb +158 -0
- data/lib/graphql/schema/input_object.rb +47 -11
- data/lib/graphql/schema/interface.rb +15 -21
- data/lib/graphql/schema/introspection_system.rb +7 -17
- data/lib/graphql/schema/late_bound_type.rb +10 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
- data/lib/graphql/schema/member/build_type.rb +11 -3
- data/lib/graphql/schema/member/has_arguments.rb +170 -130
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +100 -38
- data/lib/graphql/schema/member/has_interfaces.rb +65 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +16 -5
- data/lib/graphql/schema/printer.rb +11 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/resolver.rb +47 -32
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/subset.rb +397 -0
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema/union.rb +11 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +60 -0
- data/lib/graphql/schema/validator.rb +4 -2
- data/lib/graphql/schema/warden.rb +238 -93
- data/lib/graphql/schema.rb +498 -103
- data/lib/graphql/static_validation/all_rules.rb +2 -1
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
- data/lib/graphql/static_validation/literal_validator.rb +24 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +11 -10
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +20 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +251 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/data_dog_trace.rb +183 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
- data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +45 -0
- data/lib/graphql/tracing/platform_trace.rb +118 -0
- data/lib/graphql/tracing/platform_tracing.rb +17 -3
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/trace.rb +76 -0
- data/lib/graphql/tracing.rb +20 -40
- data/lib/graphql/type_kinds.rb +7 -4
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
- data/lib/graphql/types/relay/node_behaviors.rb +8 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
- data/lib/graphql/types/relay.rb +0 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +27 -20
- data/readme.md +13 -3
- metadata +96 -47
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -269
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -53
- data/lib/graphql/language/lexer.rl +0 -280
- data/lib/graphql/language/parser.y +0 -554
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
- data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -0,0 +1,251 @@
|
|
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
|
+
[
|
32
|
+
'lex',
|
33
|
+
'parse',
|
34
|
+
'validate',
|
35
|
+
'analyze_query',
|
36
|
+
'analyze_multiplex',
|
37
|
+
'execute_multiplex',
|
38
|
+
'execute_query',
|
39
|
+
'execute_query_lazy',
|
40
|
+
].each do |trace_method|
|
41
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
42
|
+
def #{trace_method}(**data)
|
43
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
44
|
+
layer = span_name("#{trace_method}")
|
45
|
+
kvs = metadata(data, layer)
|
46
|
+
kvs[:Key] = "#{trace_method}" if (PREP_KEYS + EXEC_KEYS).include?("#{trace_method}")
|
47
|
+
|
48
|
+
transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute'
|
49
|
+
|
50
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
51
|
+
kvs.clear # we don't have to send them twice
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
59
|
+
return_type = field.type.unwrap
|
60
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
61
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
62
|
+
else
|
63
|
+
true
|
64
|
+
end
|
65
|
+
platform_key = if trace_field
|
66
|
+
@platform_key_cache[AppOpticsTrace].platform_field_key_cache[field]
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
if platform_key && trace_field
|
71
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
72
|
+
layer = platform_key
|
73
|
+
kvs = metadata({query: query, field: field, ast_node: ast_node, arguments: arguments, object: object}, layer)
|
74
|
+
|
75
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
76
|
+
kvs.clear # we don't have to send them twice
|
77
|
+
super
|
78
|
+
end
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
85
|
+
execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
86
|
+
end
|
87
|
+
|
88
|
+
def authorized(**data)
|
89
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
90
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
|
91
|
+
kvs = metadata(data, layer)
|
92
|
+
|
93
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
94
|
+
kvs.clear # we don't have to send them twice
|
95
|
+
super
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def authorized_lazy(**data)
|
100
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
101
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
|
102
|
+
kvs = metadata(data, layer)
|
103
|
+
|
104
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
105
|
+
kvs.clear # we don't have to send them twice
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def resolve_type(**data)
|
111
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
112
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
|
113
|
+
|
114
|
+
kvs = metadata(data, layer)
|
115
|
+
|
116
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
117
|
+
kvs.clear # we don't have to send them twice
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def resolve_type_lazy(**data)
|
123
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
124
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
|
125
|
+
kvs = metadata(data, layer)
|
126
|
+
|
127
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
128
|
+
kvs.clear # we don't have to send them twice
|
129
|
+
super
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
include PlatformTrace
|
134
|
+
|
135
|
+
def platform_field_key(field)
|
136
|
+
"graphql.#{field.owner.graphql_name}.#{field.graphql_name}"
|
137
|
+
end
|
138
|
+
|
139
|
+
def platform_authorized_key(type)
|
140
|
+
"graphql.authorized.#{type.graphql_name}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def platform_resolve_type_key(type)
|
144
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def gql_config
|
150
|
+
::AppOpticsAPM::Config[:graphql] ||= {}
|
151
|
+
end
|
152
|
+
|
153
|
+
def transaction_name(query)
|
154
|
+
return if gql_config[:transaction_name] == false ||
|
155
|
+
::AppOpticsAPM::SDK.get_transaction_name
|
156
|
+
|
157
|
+
split_query = query.strip.split(/\W+/, 3)
|
158
|
+
split_query[0] = 'query' if split_query[0].empty?
|
159
|
+
name = "graphql.#{split_query[0..1].join('.')}"
|
160
|
+
|
161
|
+
::AppOpticsAPM::SDK.set_transaction_name(name)
|
162
|
+
end
|
163
|
+
|
164
|
+
def multiplex_transaction_name(names)
|
165
|
+
return if gql_config[:transaction_name] == false ||
|
166
|
+
::AppOpticsAPM::SDK.get_transaction_name
|
167
|
+
|
168
|
+
name = "graphql.multiplex.#{names.join('.')}"
|
169
|
+
name = "#{name[0..251]}..." if name.length > 254
|
170
|
+
|
171
|
+
::AppOpticsAPM::SDK.set_transaction_name(name)
|
172
|
+
end
|
173
|
+
|
174
|
+
def span_name(key)
|
175
|
+
return 'graphql.prep' if PREP_KEYS.include?(key)
|
176
|
+
return 'graphql.execute' if EXEC_KEYS.include?(key)
|
177
|
+
|
178
|
+
key[/^graphql\./] ? key : "graphql.#{key}"
|
179
|
+
end
|
180
|
+
|
181
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
182
|
+
def metadata(data, layer)
|
183
|
+
data.keys.map do |key|
|
184
|
+
case key
|
185
|
+
when :context
|
186
|
+
graphql_context(data[key], layer)
|
187
|
+
when :query
|
188
|
+
graphql_query(data[key])
|
189
|
+
when :query_string
|
190
|
+
graphql_query_string(data[key])
|
191
|
+
when :multiplex
|
192
|
+
graphql_multiplex(data[key])
|
193
|
+
when :path
|
194
|
+
[key, data[key].join(".")]
|
195
|
+
else
|
196
|
+
[key, data[key]]
|
197
|
+
end
|
198
|
+
end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
|
199
|
+
end
|
200
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
201
|
+
|
202
|
+
def graphql_context(context, layer)
|
203
|
+
context.errors && context.errors.each do |err|
|
204
|
+
AppOpticsAPM::API.log_exception(layer, err)
|
205
|
+
end
|
206
|
+
|
207
|
+
[[:Path, context.path.join('.')]]
|
208
|
+
end
|
209
|
+
|
210
|
+
def graphql_query(query)
|
211
|
+
return [] unless query
|
212
|
+
|
213
|
+
query_string = query.query_string
|
214
|
+
query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
|
215
|
+
query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
|
216
|
+
|
217
|
+
[[:InboundQuery, query_string],
|
218
|
+
[:Operation, query.selected_operation_name]]
|
219
|
+
end
|
220
|
+
|
221
|
+
def graphql_query_string(query_string)
|
222
|
+
query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
|
223
|
+
query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
|
224
|
+
|
225
|
+
[:InboundQuery, query_string]
|
226
|
+
end
|
227
|
+
|
228
|
+
def graphql_multiplex(data)
|
229
|
+
names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
|
230
|
+
multiplex_transaction_name(names) if names.size > 1
|
231
|
+
|
232
|
+
[:Operations, names.join(', ')]
|
233
|
+
end
|
234
|
+
|
235
|
+
def sanitize(query)
|
236
|
+
return unless query
|
237
|
+
|
238
|
+
# remove arguments
|
239
|
+
query.gsub(/"[^"]*"/, '"?"') # strings
|
240
|
+
.gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
|
241
|
+
.gsub(/\[[^\]]*\]/, '[?]') # arrays
|
242
|
+
end
|
243
|
+
|
244
|
+
def remove_comments(query)
|
245
|
+
return unless query
|
246
|
+
|
247
|
+
query.gsub(/#[^\n\r]*/, '')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -117,7 +117,7 @@ module GraphQL
|
|
117
117
|
else
|
118
118
|
[key, data[key]]
|
119
119
|
end
|
120
|
-
end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
|
120
|
+
end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
|
121
121
|
end
|
122
122
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
123
123
|
|
@@ -148,7 +148,7 @@ module GraphQL
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def graphql_multiplex(data)
|
151
|
-
names = data.queries.map(&:operations).map(&:keys).flatten.compact
|
151
|
+
names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
|
152
152
|
multiplex_transaction_name(names) if names.size > 1
|
153
153
|
|
154
154
|
[:Operations, names.join(', ')]
|
@@ -0,0 +1,77 @@
|
|
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
|
+
{
|
17
|
+
"lex" => "lex.graphql",
|
18
|
+
"parse" => "parse.graphql",
|
19
|
+
"validate" => "validate.graphql",
|
20
|
+
"analyze_query" => "analyze.graphql",
|
21
|
+
"analyze_multiplex" => "analyze.graphql",
|
22
|
+
"execute_multiplex" => "execute.graphql",
|
23
|
+
"execute_query" => "execute.graphql",
|
24
|
+
"execute_query_lazy" => "execute.graphql",
|
25
|
+
}.each do |trace_method, platform_key|
|
26
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
27
|
+
def #{trace_method}(**data)
|
28
|
+
#{
|
29
|
+
if trace_method == "execute_query"
|
30
|
+
<<-RUBY
|
31
|
+
set_this_txn_name = data[:query].context[:set_appsignal_action_name]
|
32
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
|
33
|
+
Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
Appsignal.instrument("#{platform_key}") do
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
RUBY
|
44
|
+
end
|
45
|
+
|
46
|
+
def platform_execute_field(platform_key)
|
47
|
+
Appsignal.instrument(platform_key) do
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def platform_authorized(platform_key)
|
53
|
+
Appsignal.instrument(platform_key) do
|
54
|
+
yield
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def platform_resolve_type(platform_key)
|
59
|
+
Appsignal.instrument(platform_key) do
|
60
|
+
yield
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def platform_field_key(field)
|
65
|
+
"#{field.owner.graphql_name}.#{field.graphql_name}.graphql"
|
66
|
+
end
|
67
|
+
|
68
|
+
def platform_authorized_key(type)
|
69
|
+
"#{type.graphql_name}.authorized.graphql"
|
70
|
+
end
|
71
|
+
|
72
|
+
def platform_resolve_type_key(type)
|
73
|
+
"#{type.graphql_name}.resolve_type.graphql"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,183 @@
|
|
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
|
+
{
|
24
|
+
'lex' => 'lex.graphql',
|
25
|
+
'parse' => 'parse.graphql',
|
26
|
+
'validate' => 'validate.graphql',
|
27
|
+
'analyze_query' => 'analyze.graphql',
|
28
|
+
'analyze_multiplex' => 'analyze.graphql',
|
29
|
+
'execute_multiplex' => 'execute.graphql',
|
30
|
+
'execute_query' => 'execute.graphql',
|
31
|
+
'execute_query_lazy' => 'execute.graphql',
|
32
|
+
}.each do |trace_method, trace_key|
|
33
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
34
|
+
def #{trace_method}(**data)
|
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}')
|
38
|
+
|
39
|
+
#{
|
40
|
+
if trace_method == 'execute_multiplex'
|
41
|
+
<<-RUBY
|
42
|
+
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
43
|
+
|
44
|
+
resource = if operations.empty?
|
45
|
+
first_query = data[:multiplex].queries.first
|
46
|
+
fallback_transaction_name(first_query && first_query.context)
|
47
|
+
else
|
48
|
+
operations
|
49
|
+
end
|
50
|
+
span.resource = resource if resource
|
51
|
+
|
52
|
+
# [Deprecated] will be removed in the future
|
53
|
+
span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled
|
54
|
+
RUBY
|
55
|
+
elsif trace_method == 'execute_query'
|
56
|
+
<<-RUBY
|
57
|
+
span.set_tag(:selected_operation_name, data[:query].selected_operation_name)
|
58
|
+
span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
|
59
|
+
span.set_tag(:query_string, data[:query].query_string)
|
60
|
+
RUBY
|
61
|
+
end
|
62
|
+
}
|
63
|
+
if @has_prepare_span
|
64
|
+
prepare_span("#{trace_method.sub("platform_", "")}", data, span)
|
65
|
+
end
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
RUBY
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute_field_span(span_key, query, field, ast_node, arguments, object)
|
73
|
+
return_type = field.type.unwrap
|
74
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
75
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
76
|
+
else
|
77
|
+
true
|
78
|
+
end
|
79
|
+
platform_key = if trace_field
|
80
|
+
@platform_key_cache[DataDogTrace].platform_field_key_cache[field]
|
81
|
+
else
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
if platform_key && trace_field
|
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
|
+
|
89
|
+
if @has_prepare_span
|
90
|
+
prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
|
91
|
+
prepare_span(span_key, prepare_span_data, span)
|
92
|
+
end
|
93
|
+
yield
|
94
|
+
end
|
95
|
+
else
|
96
|
+
yield
|
97
|
+
end
|
98
|
+
end
|
99
|
+
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
100
|
+
execute_field_span("execute_field", query, field, ast_node, arguments, object) do
|
101
|
+
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
106
|
+
execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do
|
107
|
+
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def authorized(query:, type:, object:)
|
112
|
+
authorized_span("authorized", object, type, query) do
|
113
|
+
super(query: query, type: type, object: object)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def authorized_span(span_key, object, type, query)
|
118
|
+
platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
|
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
|
+
|
123
|
+
if @has_prepare_span
|
124
|
+
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
125
|
+
end
|
126
|
+
yield
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def authorized_lazy(object:, type:, query:)
|
131
|
+
authorized_span("authorized_lazy", object, type, query) do
|
132
|
+
super(query: query, type: type, object: object)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def resolve_type(object:, type:, query:)
|
137
|
+
resolve_type_span("resolve_type", object, type, query) do
|
138
|
+
super(object: object, query: query, type: type)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def resolve_type_lazy(object:, type:, query:)
|
143
|
+
resolve_type_span("resolve_type_lazy", object, type, query) do
|
144
|
+
super(object: object, query: query, type: type)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def resolve_type_span(span_key, object, type, query)
|
149
|
+
platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
|
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
|
+
|
154
|
+
if @has_prepare_span
|
155
|
+
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
156
|
+
end
|
157
|
+
yield
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
include PlatformTrace
|
162
|
+
|
163
|
+
# Implement this method in a subclass to apply custom tags to datadog spans
|
164
|
+
# @param key [String] The event being traced
|
165
|
+
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
166
|
+
# @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
|
167
|
+
# def prepare_span(key, data, span)
|
168
|
+
# end
|
169
|
+
|
170
|
+
def platform_field_key(field)
|
171
|
+
field.path
|
172
|
+
end
|
173
|
+
|
174
|
+
def platform_authorized_key(type)
|
175
|
+
"#{type.graphql_name}.authorized"
|
176
|
+
end
|
177
|
+
|
178
|
+
def platform_resolve_type_key(type)
|
179
|
+
"#{type.graphql_name}.resolve_type"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
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,20 +56,17 @@ 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
|
65
|
+
options.fetch(:analytics_enabled, false)
|
79
66
|
end
|
80
67
|
|
81
68
|
def analytics_sample_rate
|
69
|
+
# [Deprecated] options[:analytics_sample_rate] will be removed in the future
|
82
70
|
options.fetch(:analytics_sample_rate, 1.0)
|
83
71
|
end
|
84
72
|
|
@@ -1,38 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
module
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# - If a `before_` hook returned without an error, its corresponding `after_` hook will run.
|
9
|
-
# - If the `before_` hook did _not_ run, the `after_` hook will not be called.
|
10
|
-
#
|
11
|
-
# When errors are raised from `after_` hooks:
|
12
|
-
# - Subsequent `after_` hooks _are_ called
|
13
|
-
# - The first raised error is captured; later errors are ignored
|
14
|
-
# - If an error was capture, it's re-raised after all hooks are finished
|
15
|
-
#
|
16
|
-
# Partial runs of instrumentation are possible:
|
17
|
-
# - If a `before_multiplex` hook raises an error, no `before_query` hooks will run
|
18
|
-
# - If a `before_query` hook raises an error, subsequent `before_query` hooks will not run (on any query)
|
19
|
-
def self.apply_instrumenters(multiplex)
|
20
|
-
schema = multiplex.schema
|
21
|
-
queries = multiplex.queries
|
22
|
-
query_instrumenters = schema.instrumenters[:query]
|
23
|
-
multiplex_instrumenters = schema.instrumenters[:multiplex]
|
24
|
-
|
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]
|
25
8
|
# First, run multiplex instrumentation, then query instrumentation for each query
|
26
|
-
call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
|
27
|
-
each_query_call_hooks(query_instrumenters, queries) do
|
28
|
-
|
29
|
-
yield
|
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
|
30
12
|
end
|
31
13
|
end
|
32
14
|
end
|
33
15
|
|
34
|
-
|
35
|
-
|
16
|
+
module RunHooks
|
17
|
+
module_function
|
36
18
|
# Call the before_ hooks of each query,
|
37
19
|
# Then yield if no errors.
|
38
20
|
# `call_hooks` takes care of appropriate cleanup.
|