graphql 2.0.13 → 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. 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(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,22 @@ 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
+ 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
- class PrometheusTracing < PlatformTracing
5
+ module PrometheusTrace
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
  )
@@ -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, 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
@@ -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