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,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
|
@@ -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,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "graphql/tracing/platform_trace"
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
module Tracing
|
7
|
+
# This implementation forwards events to a notification handler (i.e.
|
8
|
+
# ActiveSupport::Notifications or Dry::Monitor::Notifications)
|
9
|
+
# with a `graphql` suffix.
|
10
|
+
module NotificationsTrace
|
11
|
+
# Initialize a new NotificationsTracing instance
|
12
|
+
#
|
13
|
+
# @param engine [#instrument(key, metadata, block)] The notifications engine to use
|
14
|
+
def initialize(engine:, **rest)
|
15
|
+
@notifications_engine = engine
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
{
|
20
|
+
"lex" => "lex.graphql",
|
21
|
+
"parse" => "parse.graphql",
|
22
|
+
"validate" => "validate.graphql",
|
23
|
+
"analyze_multiplex" => "analyze_multiplex.graphql",
|
24
|
+
"analyze_query" => "analyze_query.graphql",
|
25
|
+
"execute_multiplex" => "execute_multiplex.graphql",
|
26
|
+
"execute_query" => "execute_query.graphql",
|
27
|
+
"execute_query_lazy" => "execute_query_lazy.graphql",
|
28
|
+
"execute_field" => "execute_field.graphql",
|
29
|
+
"execute_field_lazy" => "execute_field_lazy.graphql",
|
30
|
+
"authorized" => "authorized.graphql",
|
31
|
+
"authorized_lazy" => "authorized_lazy.graphql",
|
32
|
+
"resolve_type" => "resolve_type.graphql",
|
33
|
+
"resolve_type_lazy" => "resolve_type.graphql",
|
34
|
+
}.each do |trace_method, platform_key|
|
35
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
36
|
+
def #{trace_method}(**metadata, &blk)
|
37
|
+
@notifications_engine.instrument("#{platform_key}", metadata, &blk)
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
include PlatformTrace
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,118 @@
|
|
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
|
+
|
9
|
+
@platform_key_cache = Hash.new { |h, mod| h[mod] = mod::KeyCache.new }
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
module BaseKeyCache
|
14
|
+
def initialize
|
15
|
+
@platform_field_key_cache = Hash.new { |h, k| h[k] = platform_field_key(k) }
|
16
|
+
@platform_authorized_key_cache = Hash.new { |h, k| h[k] = platform_authorized_key(k) }
|
17
|
+
@platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) }
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :platform_field_key_cache, :platform_authorized_key_cache, :platform_resolve_type_key_cache
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def platform_execute_field_lazy(*args, &block)
|
25
|
+
platform_execute_field(*args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def platform_authorized_lazy(key, &block)
|
29
|
+
platform_authorized(key, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def platform_resolve_type_lazy(key, &block)
|
33
|
+
platform_resolve_type(key, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.included(child_class)
|
37
|
+
key_methods_class = Class.new {
|
38
|
+
include(child_class)
|
39
|
+
include(BaseKeyCache)
|
40
|
+
}
|
41
|
+
child_class.const_set(:KeyCache, key_methods_class)
|
42
|
+
[:execute_field, :execute_field_lazy].each do |field_trace_method|
|
43
|
+
if !child_class.method_defined?(field_trace_method)
|
44
|
+
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
45
|
+
def #{field_trace_method}(query:, field:, ast_node:, arguments:, object:)
|
46
|
+
return_type = field.type.unwrap
|
47
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
48
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
49
|
+
else
|
50
|
+
true
|
51
|
+
end
|
52
|
+
platform_key = if trace_field
|
53
|
+
@platform_key_cache[#{child_class}].platform_field_key_cache[field]
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
if platform_key && trace_field
|
58
|
+
platform_#{field_trace_method}(platform_key) do
|
59
|
+
super
|
60
|
+
end
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
RUBY
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
[:authorized, :authorized_lazy].each do |auth_trace_method|
|
71
|
+
if !child_class.method_defined?(auth_trace_method)
|
72
|
+
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
73
|
+
def #{auth_trace_method}(type:, query:, object:)
|
74
|
+
platform_key = @platform_key_cache[#{child_class}].platform_authorized_key_cache[type]
|
75
|
+
platform_#{auth_trace_method}(platform_key) do
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
[:resolve_type, :resolve_type_lazy].each do |rt_trace_method|
|
84
|
+
if !child_class.method_defined?(rt_trace_method)
|
85
|
+
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
86
|
+
def #{rt_trace_method}(query:, type:, object:)
|
87
|
+
platform_key = @platform_key_cache[#{child_class}].platform_resolve_type_key_cache[type]
|
88
|
+
platform_#{rt_trace_method}(platform_key) do
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
RUBY
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Get the transaction name based on the operation type and name if possible, or fall back to a user provided
|
100
|
+
# one. Useful for anonymous queries.
|
101
|
+
def transaction_name(query)
|
102
|
+
selected_op = query.selected_operation
|
103
|
+
txn_name = if selected_op
|
104
|
+
op_type = selected_op.operation_type
|
105
|
+
op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous"
|
106
|
+
"#{op_type}.#{op_name}"
|
107
|
+
else
|
108
|
+
"query.anonymous"
|
109
|
+
end
|
110
|
+
"GraphQL/#{txn_name}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def fallback_transaction_name(context)
|
114
|
+
context[:tracing_fallback_transaction_name]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
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(
|
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,22 @@ module GraphQL
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def self.use(schema_defn, options = {})
|
76
|
-
|
77
|
-
|
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
|
+
warn("`use(#{self.name})` is deprecated, use the equivalent `trace_with(#{trace_module.name})` instead. More info: https://graphql-ruby.org/queries/tracing.html")
|
85
|
+
schema_defn.trace_with(trace_module, **options)
|
86
|
+
else
|
87
|
+
warn("`use(#{self.name})` and `Tracing::PlatformTracing` are deprecated. Use a `trace_with(...)` module instead. More info: https://graphql-ruby.org/queries/tracing.html. Please open an issue on the GraphQL-Ruby repo if you want to discuss further!")
|
88
|
+
tracer = self.new(**options)
|
89
|
+
schema_defn.tracer(tracer, silence_deprecation_warning: true)
|
90
|
+
end
|
91
|
+
end
|
78
92
|
end
|
79
93
|
|
80
94
|
private
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
module Tracing
|
5
|
-
|
5
|
+
module PrometheusTrace
|
6
6
|
class GraphQLCollector < ::PrometheusExporter::Server::TypeCollector
|
7
7
|
def initialize
|
8
|
-
@graphql_gauge = PrometheusExporter::Metric::
|
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
|
)
|
@@ -28,5 +28,7 @@ module GraphQL
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
# Backwards-compat:
|
32
|
+
PrometheusTracing::GraphQLCollector = PrometheusTrace::GraphQLCollector
|
31
33
|
end
|
32
34
|
end
|
@@ -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)
|
28
|
+
instrument_prometheus_execution("#{platform_key}", "#{trace_method}") { super }
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
32
|
+
|
33
|
+
def platform_execute_field(platform_key, &block)
|
34
|
+
instrument_prometheus_execution(platform_key, "execute_field", &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def platform_execute_field_lazy(platform_key, &block)
|
38
|
+
instrument_prometheus_execution(platform_key, "execute_field_lazy", &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def platform_authorized(platform_key, &block)
|
42
|
+
instrument_prometheus_execution(platform_key, "authorized", &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def platform_authorized_lazy(platform_key, &block)
|
46
|
+
instrument_prometheus_execution(platform_key, "authorized_lazy", &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def platform_resolve_type(platform_key, &block)
|
50
|
+
instrument_prometheus_execution(platform_key, "resolve_type", &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def platform_resolve_type_lazy(platform_key, &block)
|
54
|
+
instrument_prometheus_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_prometheus_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
|
@@ -27,9 +27,9 @@ module GraphQL
|
|
27
27
|
super opts
|
28
28
|
end
|
29
29
|
|
30
|
-
def platform_trace(platform_key, key,
|
30
|
+
def platform_trace(platform_key, key, _data, &block)
|
31
31
|
return yield unless @keys_whitelist.include?(key)
|
32
|
-
instrument_execution(platform_key, key,
|
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,
|
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
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Tracing
|
5
|
+
module SentryTrace
|
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_sentry_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(**data)
|
17
|
+
set_this_txn_name = data[:query].context[:set_sentry_transaction_name]
|
18
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
19
|
+
Sentry.configure_scope do |scope|
|
20
|
+
scope.set_transaction_name(transaction_name(data[:query]))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
instrument_sentry_execution("graphql.execute", "execute_query", data) { super }
|
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_multiplex",
|
32
|
+
"execute_multiplex" => "graphql.execute_multiplex",
|
33
|
+
"execute_query_lazy" => "graphql.execute"
|
34
|
+
}.each do |trace_method, platform_key|
|
35
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
36
|
+
def #{trace_method}(**data)
|
37
|
+
instrument_sentry_execution("#{platform_key}", "#{trace_method}", data) { super }
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
def platform_execute_field(platform_key, &block)
|
43
|
+
instrument_sentry_execution(platform_key, "execute_field", &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def platform_execute_field_lazy(platform_key, &block)
|
47
|
+
instrument_sentry_execution(platform_key, "execute_field_lazy", &block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def platform_authorized(platform_key, &block)
|
51
|
+
instrument_sentry_execution(platform_key, "authorized", &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def platform_authorized_lazy(platform_key, &block)
|
55
|
+
instrument_sentry_execution(platform_key, "authorized_lazy", &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def platform_resolve_type(platform_key, &block)
|
59
|
+
instrument_sentry_execution(platform_key, "resolve_type", &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def platform_resolve_type_lazy(platform_key, &block)
|
63
|
+
instrument_sentry_execution(platform_key, "resolve_type_lazy", &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def platform_field_key(field)
|
67
|
+
"graphql.field.#{field.path}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def platform_authorized_key(type)
|
71
|
+
"graphql.authorized.#{type.graphql_name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def platform_resolve_type_key(type)
|
75
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def instrument_sentry_execution(platform_key, trace_method, data=nil, &block)
|
81
|
+
return yield unless Sentry.initialized?
|
82
|
+
|
83
|
+
Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
|
84
|
+
result = yield
|
85
|
+
return result unless span
|
86
|
+
|
87
|
+
span.finish
|
88
|
+
if trace_method == "execute_multiplex" && data.key?(:multiplex)
|
89
|
+
operation_names = data[:multiplex].queries.map{|q| operation_name(q) }
|
90
|
+
span.set_description(operation_names.join(", "))
|
91
|
+
elsif trace_method == "execute_query" && data.key?(:query)
|
92
|
+
span.set_description(operation_name(data[:query]))
|
93
|
+
span.set_data('graphql.document', data[:query].query_string)
|
94
|
+
span.set_data('graphql.operation.name', data[:query].selected_operation_name) if data[:query].selected_operation_name
|
95
|
+
span.set_data('graphql.operation.type', data[:query].selected_operation.operation_type)
|
96
|
+
end
|
97
|
+
|
98
|
+
result
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def operation_name(query)
|
103
|
+
selected_op = query.selected_operation
|
104
|
+
if selected_op
|
105
|
+
[selected_op.operation_type, selected_op.name].compact.join(' ')
|
106
|
+
else
|
107
|
+
'GraphQL Operation'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|