graphql 2.0.31 → 2.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) 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 +49 -0
  6. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  7. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  8. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  9. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  10. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  11. data/lib/generators/graphql/templates/base_field.erb +2 -0
  12. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  14. data/lib/generators/graphql/templates/base_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  16. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  17. data/lib/generators/graphql/templates/base_union.erb +2 -0
  18. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  19. data/lib/generators/graphql/templates/loader.erb +2 -0
  20. data/lib/generators/graphql/templates/mutation.erb +2 -0
  21. data/lib/generators/graphql/templates/node_type.erb +2 -0
  22. data/lib/generators/graphql/templates/query_type.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +5 -0
  24. data/lib/generators/graphql/type_generator.rb +1 -1
  25. data/lib/graphql/analysis/analyzer.rb +90 -0
  26. data/lib/graphql/analysis/field_usage.rb +82 -0
  27. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  28. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  29. data/lib/graphql/analysis/query_complexity.rb +263 -0
  30. data/lib/graphql/analysis/query_depth.rb +58 -0
  31. data/lib/graphql/analysis/visitor.rb +280 -0
  32. data/lib/graphql/analysis.rb +95 -1
  33. data/lib/graphql/autoload.rb +38 -0
  34. data/lib/graphql/backtrace/table.rb +118 -55
  35. data/lib/graphql/backtrace.rb +1 -19
  36. data/lib/graphql/coercion_error.rb +1 -9
  37. data/lib/graphql/current.rb +57 -0
  38. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  39. data/lib/graphql/dashboard/installable.rb +22 -0
  40. data/lib/graphql/dashboard/limiters.rb +93 -0
  41. data/lib/graphql/dashboard/operation_store.rb +199 -0
  42. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  43. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  44. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  45. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  46. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  47. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  48. data/lib/graphql/dashboard/statics/icon.png +0 -0
  49. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  50. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  51. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  52. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  53. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  54. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  65. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  66. data/lib/graphql/dashboard.rb +161 -0
  67. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  68. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  69. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  70. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  71. data/lib/graphql/dataloader/request.rb +5 -0
  72. data/lib/graphql/dataloader/source.rb +35 -12
  73. data/lib/graphql/dataloader.rb +224 -149
  74. data/lib/graphql/date_encoding_error.rb +1 -1
  75. data/lib/graphql/dig.rb +2 -1
  76. data/lib/graphql/duration_encoding_error.rb +16 -0
  77. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  78. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  79. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  80. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  81. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  82. data/lib/graphql/execution/interpreter.rb +91 -164
  83. data/lib/graphql/execution/lookahead.rb +105 -31
  84. data/lib/graphql/execution/multiplex.rb +6 -5
  85. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  86. data/lib/graphql/introspection/directive_type.rb +1 -1
  87. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  88. data/lib/graphql/introspection/entry_points.rb +11 -5
  89. data/lib/graphql/introspection/field_type.rb +1 -1
  90. data/lib/graphql/introspection/schema_type.rb +8 -11
  91. data/lib/graphql/introspection/type_type.rb +5 -5
  92. data/lib/graphql/invalid_name_error.rb +1 -1
  93. data/lib/graphql/invalid_null_error.rb +20 -17
  94. data/lib/graphql/language/block_string.rb +34 -18
  95. data/lib/graphql/language/cache.rb +13 -0
  96. data/lib/graphql/language/comment.rb +18 -0
  97. data/lib/graphql/language/definition_slice.rb +1 -1
  98. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  99. data/lib/graphql/language/lexer.rb +319 -193
  100. data/lib/graphql/language/nodes.rb +139 -77
  101. data/lib/graphql/language/parser.rb +807 -1985
  102. data/lib/graphql/language/printer.rb +324 -151
  103. data/lib/graphql/language/sanitized_printer.rb +21 -23
  104. data/lib/graphql/language/static_visitor.rb +171 -0
  105. data/lib/graphql/language/visitor.rb +62 -119
  106. data/lib/graphql/language.rb +71 -1
  107. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  108. data/lib/graphql/pagination/array_connection.rb +6 -6
  109. data/lib/graphql/pagination/connection.rb +28 -1
  110. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  111. data/lib/graphql/query/context/scoped_context.rb +101 -0
  112. data/lib/graphql/query/context.rb +81 -144
  113. data/lib/graphql/query/null_context.rb +7 -16
  114. data/lib/graphql/query/partial.rb +179 -0
  115. data/lib/graphql/query/validation_pipeline.rb +4 -4
  116. data/lib/graphql/query/variable_validation_error.rb +1 -1
  117. data/lib/graphql/query/variables.rb +3 -3
  118. data/lib/graphql/query.rb +126 -81
  119. data/lib/graphql/railtie.rb +16 -6
  120. data/lib/graphql/rake_task.rb +3 -12
  121. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  122. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  123. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  124. data/lib/graphql/rubocop.rb +2 -0
  125. data/lib/graphql/schema/addition.rb +26 -13
  126. data/lib/graphql/schema/always_visible.rb +7 -2
  127. data/lib/graphql/schema/argument.rb +73 -14
  128. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  129. data/lib/graphql/schema/build_from_definition.rb +123 -66
  130. data/lib/graphql/schema/directive/flagged.rb +4 -2
  131. data/lib/graphql/schema/directive/one_of.rb +12 -0
  132. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  133. data/lib/graphql/schema/directive.rb +55 -3
  134. data/lib/graphql/schema/enum.rb +110 -27
  135. data/lib/graphql/schema/enum_value.rb +11 -3
  136. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  137. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  138. data/lib/graphql/schema/field.rb +173 -119
  139. data/lib/graphql/schema/field_extension.rb +1 -1
  140. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  141. data/lib/graphql/schema/input_object.rb +123 -65
  142. data/lib/graphql/schema/interface.rb +34 -16
  143. data/lib/graphql/schema/introspection_system.rb +8 -17
  144. data/lib/graphql/schema/late_bound_type.rb +4 -0
  145. data/lib/graphql/schema/list.rb +3 -3
  146. data/lib/graphql/schema/loader.rb +3 -4
  147. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -1
  148. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  149. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  150. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  151. data/lib/graphql/schema/member/has_directives.rb +5 -5
  152. data/lib/graphql/schema/member/has_fields.rb +111 -16
  153. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  154. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  155. data/lib/graphql/schema/member/has_validators.rb +1 -1
  156. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  157. data/lib/graphql/schema/member/scoped.rb +19 -0
  158. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  159. data/lib/graphql/schema/member/validates_input.rb +3 -3
  160. data/lib/graphql/schema/member.rb +1 -0
  161. data/lib/graphql/schema/mutation.rb +7 -0
  162. data/lib/graphql/schema/object.rb +33 -8
  163. data/lib/graphql/schema/printer.rb +9 -7
  164. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  165. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  166. data/lib/graphql/schema/resolver.rb +45 -31
  167. data/lib/graphql/schema/scalar.rb +4 -9
  168. data/lib/graphql/schema/subscription.rb +63 -10
  169. data/lib/graphql/schema/timeout.rb +19 -2
  170. data/lib/graphql/schema/type_expression.rb +2 -2
  171. data/lib/graphql/schema/union.rb +2 -2
  172. data/lib/graphql/schema/unique_within_type.rb +1 -1
  173. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  174. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  175. data/lib/graphql/schema/validator.rb +3 -1
  176. data/lib/graphql/schema/visibility/migration.rb +188 -0
  177. data/lib/graphql/schema/visibility/profile.rb +445 -0
  178. data/lib/graphql/schema/visibility/visit.rb +190 -0
  179. data/lib/graphql/schema/visibility.rb +311 -0
  180. data/lib/graphql/schema/warden.rb +275 -103
  181. data/lib/graphql/schema.rb +924 -210
  182. data/lib/graphql/static_validation/all_rules.rb +3 -3
  183. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  184. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  185. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  186. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  187. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  188. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  189. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  190. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  191. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  192. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  193. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  194. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  195. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  196. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  197. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  198. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  199. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  200. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  201. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  202. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  203. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  204. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  205. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  206. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  207. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  208. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  209. data/lib/graphql/static_validation/validation_context.rb +21 -5
  210. data/lib/graphql/static_validation/validator.rb +9 -1
  211. data/lib/graphql/static_validation.rb +0 -1
  212. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  213. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  214. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  215. data/lib/graphql/subscriptions/event.rb +21 -4
  216. data/lib/graphql/subscriptions/serialize.rb +3 -1
  217. data/lib/graphql/subscriptions.rb +21 -17
  218. data/lib/graphql/testing/helpers.rb +161 -0
  219. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  220. data/lib/graphql/testing.rb +3 -0
  221. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  222. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  223. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  224. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  225. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  226. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  227. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  228. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  229. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  230. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  231. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  232. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  233. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  234. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  235. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  236. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  237. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  238. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  239. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  240. data/lib/graphql/tracing/null_trace.rb +9 -0
  241. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  242. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  243. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  244. data/lib/graphql/tracing/platform_trace.rb +5 -0
  245. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  246. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  247. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  248. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  249. data/lib/graphql/tracing/scout_trace.rb +32 -55
  250. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  251. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  252. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  253. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  254. data/lib/graphql/tracing/trace.rb +112 -1
  255. data/lib/graphql/tracing.rb +31 -28
  256. data/lib/graphql/type_kinds.rb +2 -1
  257. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  258. data/lib/graphql/types/relay/connection_behaviors.rb +43 -3
  259. data/lib/graphql/types/relay/edge_behaviors.rb +18 -1
  260. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  261. data/lib/graphql/types.rb +18 -10
  262. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  263. data/lib/graphql/version.rb +1 -1
  264. data/lib/graphql.rb +68 -54
  265. data/readme.md +12 -2
  266. metadata +220 -40
  267. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  268. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  269. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  270. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  271. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  272. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  273. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  274. data/lib/graphql/analysis/ast.rb +0 -81
  275. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  276. data/lib/graphql/backtrace/trace.rb +0 -96
  277. data/lib/graphql/backtrace/tracer.rb +0 -80
  278. data/lib/graphql/deprecation.rb +0 -9
  279. data/lib/graphql/filter.rb +0 -59
  280. data/lib/graphql/language/parser.y +0 -560
  281. data/lib/graphql/language/token.rb +0 -34
  282. data/lib/graphql/schema/base_64_bp.rb +0 -26
  283. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  284. data/lib/graphql/schema/null_mask.rb +0 -11
  285. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  286. data/lib/graphql/static_validation/type_stack.rb +0 -216
  287. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,183 +1,71 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/tracing/monitor_trace"
2
3
 
3
4
  module GraphQL
4
5
  module Tracing
6
+ # A tracer for reporting to DataDog
7
+ # @example Adding this tracer to your schema
8
+ # class MySchema < GraphQL::Schema
9
+ # trace_with GraphQL::Tracing::DataDogTrace
10
+ # end
11
+ # @example Skipping `resolve_type` and `authorized` events
12
+ # trace_with GraphQL::Tracing::DataDogTrace, trace_authorized: false, trace_resolve_type: false
13
+ DataDogTrace = MonitorTrace.create_module("datadog")
5
14
  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
15
+ class DatadogMonitor < MonitorTrace::Monitor
16
+ def initialize(set_transaction_name:, service: nil, tracer: nil, **_rest)
17
+ super
18
+ if tracer.nil?
19
+ tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
20
+ end
21
+ @tracer = tracer
22
+ @service_name = service
23
+ @has_prepare_span = @trace.respond_to?(:prepare_span)
12
24
  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
25
 
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
26
+ attr_reader :tracer, :service_name
51
27
 
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)
28
+ def instrument(keyword, object)
29
+ trace_key = name_for(keyword, object)
30
+ @tracer.trace(trace_key, service: @service_name, type: 'custom') do |span|
31
+ span.set_tag('component', 'graphql')
32
+ op_name = keyword.respond_to?(:name) ? keyword.name : keyword.to_s
33
+ span.set_tag('operation', op_name)
34
+
35
+ if keyword == :execute
36
+ operations = object.queries.map(&:selected_operation_name).join(', ')
37
+ first_query = object.queries.first
38
+ resource = if operations.empty?
39
+ fallback_transaction_name(first_query && first_query.context)
40
+ else
41
+ operations
65
42
  end
66
- super
67
- end
68
- end
69
- RUBY
70
- end
43
+ span.resource = resource if resource
71
44
 
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)
45
+ span.set_tag("selected_operation_name", first_query.selected_operation_name)
46
+ span.set_tag("selected_operation_type", first_query.selected_operation&.operation_type)
47
+ span.set_tag("query_string", first_query.query_string)
48
+ end
88
49
 
89
50
  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)
51
+ @trace.prepare_span(keyword, object, span)
92
52
  end
93
53
  yield
94
54
  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
55
  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
56
 
123
- if @has_prepare_span
124
- prepare_span(span_key, {object: object, type: type, query: query}, span)
57
+ include MonitorTrace::Monitor::GraphQLSuffixNames
58
+ class Event < MonitorTrace::Monitor::Event
59
+ def start
60
+ name = @monitor.name_for(keyword, object)
61
+ @dd_span = @monitor.tracer.trace(name, service: @monitor.service_name, type: 'custom')
125
62
  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
63
 
154
- if @has_prepare_span
155
- prepare_span(span_key, {object: object, type: type, query: query}, span)
64
+ def finish
65
+ @dd_span.finish
156
66
  end
157
- yield
158
67
  end
159
68
  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
69
  end
182
70
  end
183
71
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  class DataDogTracing < PlatformTracing
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ class DetailedTrace
6
+ # An in-memory trace storage backend. Suitable for testing and development only.
7
+ # It won't work for multi-process deployments and everything is erased when the app is restarted.
8
+ class MemoryBackend
9
+ def initialize(limit: nil)
10
+ @limit = limit
11
+ @traces = {}
12
+ @next_id = 0
13
+ end
14
+
15
+ def traces(last:, before:)
16
+ page = []
17
+ @traces.values.reverse_each do |trace|
18
+ if page.size == last
19
+ break
20
+ elsif before.nil? || trace.begin_ms < before
21
+ page << trace
22
+ end
23
+ end
24
+ page
25
+ end
26
+
27
+ def find_trace(id)
28
+ @traces[id]
29
+ end
30
+
31
+ def delete_trace(id)
32
+ @traces.delete(id.to_i)
33
+ nil
34
+ end
35
+
36
+ def delete_all_traces
37
+ @traces.clear
38
+ nil
39
+ end
40
+
41
+ def save_trace(operation_name, duration, begin_ms, trace_data)
42
+ id = @next_id
43
+ @next_id += 1
44
+ @traces[id] = DetailedTrace::StoredTrace.new(
45
+ id: id,
46
+ operation_name: operation_name,
47
+ duration_ms: duration,
48
+ begin_ms: begin_ms,
49
+ trace_data: trace_data
50
+ )
51
+ if @limit && @traces.size > @limit
52
+ del_keys = @traces.keys[0...-@limit]
53
+ del_keys.each { |k| @traces.delete(k) }
54
+ end
55
+ id
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ class DetailedTrace
6
+ class RedisBackend
7
+ KEY_PREFIX = "gql:trace:"
8
+ def initialize(redis:, limit: nil)
9
+ @redis = redis
10
+ @key = KEY_PREFIX + "traces"
11
+ @remrangebyrank_limit = limit ? -limit - 1 : nil
12
+ end
13
+
14
+ def traces(last:, before:)
15
+ before = case before
16
+ when Numeric
17
+ "(#{before}"
18
+ when nil
19
+ "+inf"
20
+ end
21
+ str_pairs = @redis.zrange(@key, before, 0, byscore: true, rev: true, limit: [0, last || 100], withscores: true)
22
+ str_pairs.map do |(str_data, score)|
23
+ entry_to_trace(score, str_data)
24
+ end
25
+ end
26
+
27
+ def delete_trace(id)
28
+ @redis.zremrangebyscore(@key, id, id)
29
+ nil
30
+ end
31
+
32
+ def delete_all_traces
33
+ @redis.del(@key)
34
+ end
35
+
36
+ def find_trace(id)
37
+ str_data = @redis.zrange(@key, id, id, byscore: true).first
38
+ if str_data.nil?
39
+ nil
40
+ else
41
+ entry_to_trace(id, str_data)
42
+ end
43
+ end
44
+
45
+ def save_trace(operation_name, duration_ms, begin_ms, trace_data)
46
+ id = begin_ms
47
+ data = JSON.dump({ "o" => operation_name, "d" => duration_ms, "b" => begin_ms, "t" => Base64.encode64(trace_data) })
48
+ @redis.pipelined do |pipeline|
49
+ pipeline.zadd(@key, id, data)
50
+ if @remrangebyrank_limit
51
+ pipeline.zremrangebyrank(@key, 0, @remrangebyrank_limit)
52
+ end
53
+ end
54
+ id
55
+ end
56
+
57
+ private
58
+
59
+ def entry_to_trace(id, json_str)
60
+ data = JSON.parse(json_str)
61
+ StoredTrace.new(
62
+ id: id,
63
+ operation_name: data["o"],
64
+ duration_ms: data["d"].to_f,
65
+ begin_ms: data["b"].to_i,
66
+ trace_data: Base64.decode64(data["t"]),
67
+ )
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/tracing/detailed_trace/memory_backend"
3
+ require "graphql/tracing/detailed_trace/redis_backend"
4
+
5
+ module GraphQL
6
+ module Tracing
7
+ # `DetailedTrace` can make detailed profiles for a subset of production traffic.
8
+ #
9
+ # When `MySchema.detailed_trace?(query)` returns `true`, a profiler-specific `trace_mode: ...` will be used for the query,
10
+ # overriding the one in `context[:trace_mode]`.
11
+ #
12
+ # By default, the detailed tracer calls `.inspect` on application objects returned from fields. You can customize
13
+ # this behavior by extending {DetailedTrace} and overriding {#inspect_object}. You can opt out of debug annotations
14
+ # entirely with `use ..., debug: false` or for a single query with `context: { detailed_trace_debug: false }`.
15
+ #
16
+ # __Redis__: The sampler stores its results in a provided Redis database. Depending on your needs,
17
+ # You can configure this database to retain all data (persistent) or to expire data according to your rules.
18
+ # If you need to save traces indefinitely, you can download them from Perfetto after opening them there.
19
+ #
20
+ # @example Adding the sampler to your schema
21
+ # class MySchema < GraphQL::Schema
22
+ # # Add the sampler:
23
+ # use GraphQL::Tracing::DetailedTrace, redis: Redis.new(...), limit: 100
24
+ #
25
+ # # And implement this hook to tell it when to take a sample:
26
+ # def self.detailed_trace?(query)
27
+ # # Could use `query.context`, `query.selected_operation_name`, `query.query_string` here
28
+ # # Could call out to Flipper, etc
29
+ # rand <= 0.000_1 # one in ten thousand
30
+ # end
31
+ # end
32
+ #
33
+ # @see Graphql::Dashboard GraphQL::Dashboard for viewing stored results
34
+ #
35
+ # @example Customizing debug output in traces
36
+ # class CustomDetailedTrace < GraphQL::Tracing::DetailedTrace
37
+ # def inspect_object(object)
38
+ # if object.is_a?(SomeThing)
39
+ # # handle it specially ...
40
+ # else
41
+ # super
42
+ # end
43
+ # end
44
+ # end
45
+ #
46
+ # @example disabling debug annotations completely
47
+ # use DetailedTrace, debug: false, ...
48
+ #
49
+ # @example disabling debug annotations for one query
50
+ # MySchema.execute(query_str, context: { detailed_trace_debug: false })
51
+ #
52
+ class DetailedTrace
53
+ # @param redis [Redis] If provided, profiles will be stored in Redis for later review
54
+ # @param limit [Integer] A maximum number of profiles to store
55
+ # @param debug [Boolean] if `false`, it won't create `debug` annotations in Perfetto traces (reduces overhead)
56
+ def self.use(schema, trace_mode: :profile_sample, memory: false, debug: debug?, redis: nil, limit: nil)
57
+ storage = if redis
58
+ RedisBackend.new(redis: redis, limit: limit)
59
+ elsif memory
60
+ MemoryBackend.new(limit: limit)
61
+ else
62
+ raise ArgumentError, "Pass `redis: ...` to store traces in Redis for later review"
63
+ end
64
+ detailed_trace = self.new(storage: storage, trace_mode: trace_mode, debug: debug)
65
+ schema.detailed_trace = detailed_trace
66
+ schema.trace_with(PerfettoTrace, mode: trace_mode, save_profile: true)
67
+ end
68
+
69
+ def initialize(storage:, trace_mode:, debug:)
70
+ @storage = storage
71
+ @trace_mode = trace_mode
72
+ @debug = debug
73
+ end
74
+
75
+ # @return [Symbol] The trace mode to use when {Schema.detailed_trace?} returns `true`
76
+ attr_reader :trace_mode
77
+
78
+ # @return [String] ID of saved trace
79
+ def save_trace(operation_name, duration_ms, begin_ms, trace_data)
80
+ @storage.save_trace(operation_name, duration_ms, begin_ms, trace_data)
81
+ end
82
+
83
+ # @return [Boolean]
84
+ def debug?
85
+ @debug
86
+ end
87
+
88
+ # @param last [Integer]
89
+ # @param before [Integer] Timestamp in milliseconds since epoch
90
+ # @return [Enumerable<StoredTrace>]
91
+ def traces(last: nil, before: nil)
92
+ @storage.traces(last: last, before: before)
93
+ end
94
+
95
+ # @return [StoredTrace, nil]
96
+ def find_trace(id)
97
+ @storage.find_trace(id)
98
+ end
99
+
100
+ # @return [void]
101
+ def delete_trace(id)
102
+ @storage.delete_trace(id)
103
+ end
104
+
105
+ # @return [void]
106
+ def delete_all_traces
107
+ @storage.delete_all_traces
108
+ end
109
+
110
+ def inspect_object(object)
111
+ self.class.inspect_object(object)
112
+ end
113
+
114
+ def self.inspect_object(object)
115
+ if defined?(ActiveRecord::Relation) && object.is_a?(ActiveRecord::Relation)
116
+ "#{object.class}, .to_sql=#{object.to_sql.inspect}"
117
+ else
118
+ object.inspect
119
+ end
120
+ end
121
+
122
+ # Default debug setting
123
+ # @return [true]
124
+ def self.debug?
125
+ true
126
+ end
127
+
128
+ class StoredTrace
129
+ def initialize(id:, operation_name:, duration_ms:, begin_ms:, trace_data:)
130
+ @id = id
131
+ @operation_name = operation_name
132
+ @duration_ms = duration_ms
133
+ @begin_ms = begin_ms
134
+ @trace_data = trace_data
135
+ end
136
+
137
+ attr_reader :id, :operation_name, :duration_ms, :begin_ms, :trace_data
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ module LegacyHooksTrace
6
+ def execute_multiplex(multiplex:)
7
+ multiplex_instrumenters = multiplex.schema.instrumenters[:multiplex]
8
+ query_instrumenters = multiplex.schema.instrumenters[:query]
9
+ # First, run multiplex instrumentation, then query instrumentation for each query
10
+ RunHooks.call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
11
+ RunHooks.each_query_call_hooks(query_instrumenters, multiplex.queries) do
12
+ super
13
+ end
14
+ end
15
+ end
16
+
17
+ module RunHooks
18
+ module_function
19
+ # Call the before_ hooks of each query,
20
+ # Then yield if no errors.
21
+ # `call_hooks` takes care of appropriate cleanup.
22
+ def each_query_call_hooks(instrumenters, queries, i = 0)
23
+ if i >= queries.length
24
+ yield
25
+ else
26
+ query = queries[i]
27
+ call_hooks(instrumenters, query, :before_query, :after_query) {
28
+ each_query_call_hooks(instrumenters, queries, i + 1) {
29
+ yield
30
+ }
31
+ }
32
+ end
33
+ end
34
+
35
+ # Call each before hook, and if they all succeed, yield.
36
+ # If they don't all succeed, call after_ for each one that succeeded.
37
+ def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
38
+ begin
39
+ successful = []
40
+ instrumenters.each do |instrumenter|
41
+ instrumenter.public_send(before_hook_name, object)
42
+ successful << instrumenter
43
+ end
44
+
45
+ # if any before hooks raise an exception, quit calling before hooks,
46
+ # but call the after hooks on anything that succeeded but also
47
+ # raise the exception that came from the before hook.
48
+ rescue GraphQL::ExecutionError => err
49
+ object.context.errors << err
50
+ rescue => e
51
+ raise call_after_hooks(successful, object, after_hook_name, e)
52
+ end
53
+
54
+ begin
55
+ yield # Call the user code
56
+ ensure
57
+ ex = call_after_hooks(successful, object, after_hook_name, nil)
58
+ raise ex if ex
59
+ end
60
+ end
61
+
62
+ def call_after_hooks(instrumenters, object, after_hook_name, ex)
63
+ instrumenters.reverse_each do |instrumenter|
64
+ begin
65
+ instrumenter.public_send(after_hook_name, object)
66
+ rescue => e
67
+ ex = e
68
+ end
69
+ end
70
+ ex
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,67 +1,10 @@
1
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
2
 
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
3
+ require "graphql/tracing/trace"
4
+ require "graphql/tracing/call_legacy_tracers"
64
5
 
6
+ module GraphQL
7
+ module Tracing
65
8
  class LegacyTrace < Trace
66
9
  include CallLegacyTracers
67
10
  end