graphql 2.2.17 → 2.5.16

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 (240) 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_generator.rb +46 -0
  4. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  5. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  6. data/lib/generators/graphql/templates/schema.erb +3 -0
  7. data/lib/generators/graphql/type_generator.rb +1 -1
  8. data/lib/graphql/analysis/analyzer.rb +90 -0
  9. data/lib/graphql/analysis/field_usage.rb +82 -0
  10. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  11. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  12. data/lib/graphql/analysis/query_complexity.rb +263 -0
  13. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  14. data/lib/graphql/analysis/visitor.rb +280 -0
  15. data/lib/graphql/analysis.rb +95 -1
  16. data/lib/graphql/autoload.rb +38 -0
  17. data/lib/graphql/backtrace/table.rb +118 -55
  18. data/lib/graphql/backtrace.rb +1 -19
  19. data/lib/graphql/current.rb +57 -0
  20. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  21. data/lib/graphql/dashboard/installable.rb +22 -0
  22. data/lib/graphql/dashboard/limiters.rb +93 -0
  23. data/lib/graphql/dashboard/operation_store.rb +199 -0
  24. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  25. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  26. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  27. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  28. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  29. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  30. data/lib/graphql/dashboard/statics/icon.png +0 -0
  31. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  42. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  43. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  44. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  45. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  46. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  47. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  48. data/lib/graphql/dashboard.rb +158 -0
  49. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  50. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  51. data/lib/graphql/dataloader/async_dataloader.rb +46 -19
  52. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  53. data/lib/graphql/dataloader/source.rb +20 -9
  54. data/lib/graphql/dataloader.rb +153 -45
  55. data/lib/graphql/date_encoding_error.rb +1 -1
  56. data/lib/graphql/dig.rb +2 -1
  57. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  58. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  59. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  60. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +63 -5
  61. data/lib/graphql/execution/interpreter/runtime.rb +321 -222
  62. data/lib/graphql/execution/interpreter.rb +23 -30
  63. data/lib/graphql/execution/lookahead.rb +18 -11
  64. data/lib/graphql/execution/multiplex.rb +6 -5
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +1 -1
  67. data/lib/graphql/introspection/entry_points.rb +2 -2
  68. data/lib/graphql/introspection/field_type.rb +1 -1
  69. data/lib/graphql/introspection/schema_type.rb +6 -11
  70. data/lib/graphql/introspection/type_type.rb +5 -5
  71. data/lib/graphql/invalid_name_error.rb +1 -1
  72. data/lib/graphql/invalid_null_error.rb +20 -17
  73. data/lib/graphql/language/cache.rb +13 -0
  74. data/lib/graphql/language/comment.rb +18 -0
  75. data/lib/graphql/language/document_from_schema_definition.rb +64 -35
  76. data/lib/graphql/language/lexer.rb +72 -42
  77. data/lib/graphql/language/nodes.rb +93 -52
  78. data/lib/graphql/language/parser.rb +168 -61
  79. data/lib/graphql/language/printer.rb +31 -15
  80. data/lib/graphql/language/sanitized_printer.rb +1 -1
  81. data/lib/graphql/language.rb +61 -1
  82. data/lib/graphql/pagination/connection.rb +1 -1
  83. data/lib/graphql/query/context/scoped_context.rb +1 -1
  84. data/lib/graphql/query/context.rb +46 -47
  85. data/lib/graphql/query/null_context.rb +3 -5
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query/validation_pipeline.rb +2 -2
  88. data/lib/graphql/query/variable_validation_error.rb +1 -1
  89. data/lib/graphql/query.rb +123 -69
  90. data/lib/graphql/railtie.rb +7 -0
  91. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  92. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  93. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  94. data/lib/graphql/rubocop.rb +2 -0
  95. data/lib/graphql/schema/addition.rb +26 -13
  96. data/lib/graphql/schema/always_visible.rb +7 -2
  97. data/lib/graphql/schema/argument.rb +57 -8
  98. data/lib/graphql/schema/build_from_definition.rb +116 -49
  99. data/lib/graphql/schema/directive/flagged.rb +4 -2
  100. data/lib/graphql/schema/directive.rb +54 -2
  101. data/lib/graphql/schema/enum.rb +107 -24
  102. data/lib/graphql/schema/enum_value.rb +10 -2
  103. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  104. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  105. data/lib/graphql/schema/field.rb +134 -45
  106. data/lib/graphql/schema/field_extension.rb +1 -1
  107. data/lib/graphql/schema/has_single_input_argument.rb +6 -2
  108. data/lib/graphql/schema/input_object.rb +122 -64
  109. data/lib/graphql/schema/interface.rb +23 -5
  110. data/lib/graphql/schema/introspection_system.rb +6 -17
  111. data/lib/graphql/schema/late_bound_type.rb +4 -0
  112. data/lib/graphql/schema/list.rb +3 -3
  113. data/lib/graphql/schema/loader.rb +3 -2
  114. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  115. data/lib/graphql/schema/member/has_arguments.rb +44 -58
  116. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  117. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  118. data/lib/graphql/schema/member/has_directives.rb +4 -4
  119. data/lib/graphql/schema/member/has_fields.rb +26 -6
  120. data/lib/graphql/schema/member/has_interfaces.rb +6 -6
  121. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  122. data/lib/graphql/schema/member/has_validators.rb +1 -1
  123. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  124. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  125. data/lib/graphql/schema/member.rb +1 -0
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +25 -8
  128. data/lib/graphql/schema/printer.rb +1 -0
  129. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  130. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  131. data/lib/graphql/schema/resolver.rb +29 -23
  132. data/lib/graphql/schema/scalar.rb +1 -6
  133. data/lib/graphql/schema/subscription.rb +52 -6
  134. data/lib/graphql/schema/timeout.rb +19 -2
  135. data/lib/graphql/schema/type_expression.rb +2 -2
  136. data/lib/graphql/schema/union.rb +1 -1
  137. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  138. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  139. data/lib/graphql/schema/validator.rb +3 -1
  140. data/lib/graphql/schema/visibility/migration.rb +188 -0
  141. data/lib/graphql/schema/visibility/profile.rb +445 -0
  142. data/lib/graphql/schema/visibility/visit.rb +190 -0
  143. data/lib/graphql/schema/visibility.rb +311 -0
  144. data/lib/graphql/schema/warden.rb +190 -20
  145. data/lib/graphql/schema.rb +695 -167
  146. data/lib/graphql/static_validation/all_rules.rb +2 -2
  147. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  148. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  149. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  150. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  151. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  152. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  153. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  154. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  155. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  156. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  157. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  158. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  159. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  160. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  161. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  162. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  163. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  164. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  165. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  166. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  167. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  168. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  169. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  170. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  171. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  172. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  173. data/lib/graphql/static_validation/validation_context.rb +18 -2
  174. data/lib/graphql/static_validation/validator.rb +6 -1
  175. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -3
  176. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  177. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  178. data/lib/graphql/subscriptions/event.rb +13 -2
  179. data/lib/graphql/subscriptions/serialize.rb +1 -1
  180. data/lib/graphql/subscriptions.rb +7 -5
  181. data/lib/graphql/testing/helpers.rb +48 -16
  182. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  183. data/lib/graphql/testing.rb +1 -0
  184. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  185. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  186. data/lib/graphql/tracing/appoptics_trace.rb +5 -1
  187. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  188. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  189. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  190. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  191. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  192. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  193. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  194. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  195. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  196. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  197. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  198. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  199. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  200. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  201. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  202. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  203. data/lib/graphql/tracing/null_trace.rb +9 -0
  204. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  205. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  206. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  207. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  208. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  209. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  210. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  211. data/lib/graphql/tracing/scout_trace.rb +32 -58
  212. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  213. data/lib/graphql/tracing/sentry_trace.rb +64 -98
  214. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  215. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  216. data/lib/graphql/tracing/trace.rb +111 -1
  217. data/lib/graphql/tracing.rb +31 -30
  218. data/lib/graphql/type_kinds.rb +2 -1
  219. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  220. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  221. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  222. data/lib/graphql/types.rb +18 -11
  223. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  224. data/lib/graphql/version.rb +1 -1
  225. data/lib/graphql.rb +64 -54
  226. metadata +197 -22
  227. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  228. data/lib/graphql/analysis/ast/field_usage.rb +0 -82
  229. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  230. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  231. data/lib/graphql/analysis/ast/query_complexity.rb +0 -182
  232. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  233. data/lib/graphql/analysis/ast.rb +0 -94
  234. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  235. data/lib/graphql/backtrace/trace.rb +0 -93
  236. data/lib/graphql/backtrace/tracer.rb +0 -80
  237. data/lib/graphql/language/token.rb +0 -34
  238. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  239. data/lib/graphql/schema/null_mask.rb +0 -11
  240. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -86,7 +86,7 @@ module GraphQL
86
86
  else
87
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
88
  tracer = self.new(**options)
89
- schema_defn.tracer(tracer)
89
+ schema_defn.tracer(tracer, silence_deprecation_warning: true)
90
90
  end
91
91
  end
92
92
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  module PrometheusTrace
@@ -1,91 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Prometheus.
8
+ #
9
+ # The PrometheusExporter server must be run with a custom type collector that extends `GraphQL::Tracing::PrometheusTracing::GraphQLCollector`.
10
+ #
11
+ # @example Adding this trace to your schema
12
+ # require 'prometheus_exporter/client'
13
+ #
14
+ # class MySchema < GraphQL::Schema
15
+ # trace_with GraphQL::Tracing::PrometheusTrace
16
+ # end
17
+ #
18
+ # @example Running a custom type collector
19
+ # # lib/graphql_collector.rb
20
+ # if defined?(PrometheusExporter::Server)
21
+ # require 'graphql/tracing'
22
+ #
23
+ # class GraphQLCollector < GraphQL::Tracing::PrometheusTrace::GraphQLCollector
24
+ # end
25
+ # end
26
+ #
27
+ # # Then run:
28
+ # # bundle exec prometheus_exporter -a lib/graphql_collector.rb
29
+ PrometheusTrace = MonitorTrace.create_module("prometheus")
5
30
  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
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
- {
19
- 'lex' => "graphql.lex",
20
- 'parse' => "graphql.parse",
21
- 'validate' => "graphql.validate",
22
- 'analyze_query' => "graphql.analyze",
23
- 'analyze_multiplex' => "graphql.analyze",
24
- 'execute_multiplex' => "graphql.execute",
25
- 'execute_query' => "graphql.execute",
26
- 'execute_query_lazy' => "graphql.execute",
27
- }.each do |trace_method, platform_key|
28
- module_eval <<-RUBY, __FILE__, __LINE__
29
- def #{trace_method}(**data, &block)
30
- instrument_execution("#{platform_key}", "#{trace_method}", &block)
31
- end
32
- RUBY
31
+ if defined?(PrometheusExporter::Server)
32
+ autoload :GraphQLCollector, "graphql/tracing/prometheus_trace/graphql_collector"
33
33
  end
34
34
 
35
- # rubocop:enable Development/NoEvalCop
36
-
37
- def platform_execute_field(platform_key, &block)
38
- instrument_execution(platform_key, "execute_field", &block)
35
+ def initialize(client: PrometheusExporter::Client.default, keys_whitelist: [:execute_field], collector_type: "graphql", **rest)
36
+ @prometheus_client = client
37
+ @prometheus_keys_whitelist = keys_whitelist.map(&:to_sym) # handle previous string keys
38
+ @prometheus_collector_type = collector_type
39
+ setup_prometheus_monitor(**rest)
40
+ super
39
41
  end
40
42
 
41
- def platform_execute_field_lazy(platform_key, &block)
42
- instrument_execution(platform_key, "execute_field_lazy", &block)
43
- end
44
-
45
- def platform_authorized(platform_key, &block)
46
- instrument_execution(platform_key, "authorized", &block)
47
- end
48
-
49
- def platform_authorized_lazy(platform_key, &block)
50
- instrument_execution(platform_key, "authorized_lazy", &block)
51
- end
52
-
53
- def platform_resolve_type(platform_key, &block)
54
- instrument_execution(platform_key, "resolve_type", &block)
55
- end
43
+ attr_reader :prometheus_collector_type, :prometheus_client, :prometheus_keys_whitelist
44
+
45
+ class PrometheusMonitor < MonitorTrace::Monitor
46
+ def instrument(keyword, object)
47
+ if active?(keyword)
48
+ start = gettime
49
+ result = yield
50
+ duration = gettime - start
51
+ send_json(duration, keyword, object)
52
+ result
53
+ else
54
+ yield
55
+ end
56
+ end
56
57
 
57
- def platform_resolve_type_lazy(platform_key, &block)
58
- instrument_execution(platform_key, "resolve_type_lazy", &block)
59
- end
58
+ def active?(keyword)
59
+ @trace.prometheus_keys_whitelist.include?(keyword)
60
+ end
60
61
 
61
- def platform_field_key(field)
62
- field.path
63
- end
62
+ def gettime
63
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
64
+ end
64
65
 
65
- def platform_authorized_key(type)
66
- "#{type.graphql_name}.authorized"
67
- end
66
+ def send_json(duration, keyword, object)
67
+ event_name = name_for(keyword, object)
68
+ @trace.prometheus_client.send_json(
69
+ type: @trace.prometheus_collector_type,
70
+ duration: duration,
71
+ platform_key: event_name,
72
+ key: keyword
73
+ )
74
+ end
68
75
 
69
- def platform_resolve_type_key(type)
70
- "#{type.graphql_name}.resolve_type"
71
- end
76
+ include MonitorTrace::Monitor::GraphQLPrefixNames
72
77
 
73
- private
78
+ class Event < MonitorTrace::Monitor::Event
79
+ def start
80
+ @start_time = @monitor.gettime
81
+ end
74
82
 
75
- def instrument_execution(platform_key, key, &block)
76
- if @keys_whitelist.include?(key)
77
- start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
78
- result = block.call
79
- duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
80
- @client.send_json(
81
- type: @collector_type,
82
- duration: duration,
83
- platform_key: platform_key,
84
- key: key
85
- )
86
- result
87
- else
88
- yield
83
+ def finish
84
+ if @monitor.active?(keyword)
85
+ duration = @monitor.gettime - @start_time
86
+ @monitor.send_json(duration, keyword, object)
87
+ end
88
+ end
89
89
  end
90
90
  end
91
91
  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 PrometheusTracing < PlatformTracing
@@ -1,74 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for sending GraphQL-Ruby times to Scout
8
+ #
9
+ # @example Adding this tracer to your schema
10
+ # class MySchema < GraphQL::Schema
11
+ # trace_with GraphQL::Tracing::ScoutTrace
12
+ # end
13
+ ScoutTrace = MonitorTrace.create_module("scout")
5
14
  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
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
-
21
- {
22
- "lex" => "lex.graphql",
23
- "parse" => "parse.graphql",
24
- "validate" => "validate.graphql",
25
- "analyze_query" => "analyze.graphql",
26
- "analyze_multiplex" => "analyze.graphql",
27
- "execute_multiplex" => "execute.graphql",
28
- "execute_query" => "execute.graphql",
29
- "execute_query_lazy" => "execute.graphql",
30
- }.each do |trace_method, platform_key|
31
- module_eval <<-RUBY, __FILE__, __LINE__
32
- def #{trace_method}(**data)
33
- #{
34
- if trace_method == "execute_query"
35
- <<-RUBY
36
- set_this_txn_name = data[:query].context[:set_scout_transaction_name]
37
- if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
38
- ScoutApm::Transaction.rename(transaction_name(data[:query]))
39
- end
40
- RUBY
15
+ class ScoutMonitor < MonitorTrace::Monitor
16
+ def instrument(keyword, object)
17
+ if keyword == :execute
18
+ query = object.queries.first
19
+ set_this_txn_name = query.context[:set_scout_transaction_name]
20
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
21
+ ScoutApm::Transaction.rename(transaction_name(query))
41
22
  end
42
- }
23
+ end
43
24
 
44
- self.class.instrument("GraphQL", "#{platform_key}", INSTRUMENT_OPTS) do
45
- super
25
+ ScoutApm::Tracer.instrument("GraphQL", name_for(keyword, object), INSTRUMENT_OPTS) do
26
+ yield
46
27
  end
47
28
  end
48
- RUBY
49
- end
50
- # rubocop:enable Development/NoEvalCop
51
29
 
52
- def platform_execute_field(platform_key, &block)
53
- self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
54
- end
30
+ INSTRUMENT_OPTS = { scope: true }
55
31
 
56
- def platform_authorized(platform_key, &block)
57
- self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
58
- end
59
-
60
- alias :platform_resolve_type :platform_authorized
61
-
62
- def platform_field_key(field)
63
- field.path
64
- end
32
+ include MonitorTrace::Monitor::GraphQLSuffixNames
65
33
 
66
- def platform_authorized_key(type)
67
- "#{type.graphql_name}.authorized"
68
- end
34
+ class Event < MonitorTrace::Monitor::Event
35
+ def start
36
+ layer = ScoutApm::Layer.new("GraphQL", @monitor.name_for(keyword, object))
37
+ layer.subscopable!
38
+ @scout_req = ScoutApm::RequestManager.lookup
39
+ @scout_req.start_layer(layer)
40
+ end
69
41
 
70
- def platform_resolve_type_key(type)
71
- "#{type.graphql_name}.resolve_type"
42
+ def finish
43
+ @scout_req.stop_layer
44
+ end
45
+ end
72
46
  end
73
47
  end
74
48
  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 ScoutTracing < PlatformTracing
@@ -1,114 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Sentry.
8
+ #
9
+ # @example Installing the tracer
10
+ # class MySchema < GraphQL::Schema
11
+ # trace_with GraphQL::Tracing::SentryTrace
12
+ # end
13
+ # @see MonitorTrace Configuration Options in the parent module
14
+ SentryTrace = MonitorTrace.create_module("sentry")
5
15
  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]))
16
+ class SentryMonitor < MonitorTrace::Monitor
17
+ def instrument(keyword, object)
18
+ return yield unless Sentry.initialized?
19
+
20
+ platform_key = name_for(keyword, object)
21
+
22
+ Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
23
+ result = yield
24
+ return result unless span
25
+
26
+ span.finish
27
+ if keyword == :execute
28
+ queries = object.queries
29
+ operation_names = queries.map{|q| operation_name(q) }
30
+ span.set_description(operation_names.join(", "))
31
+
32
+ if queries.size == 1
33
+ query = queries.first
34
+ set_this_txn_name = query.context[:set_sentry_transaction_name]
35
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
36
+ Sentry.configure_scope do |scope|
37
+ scope.set_transaction_name(transaction_name(query))
38
+ end
39
+ end
40
+ span.set_data('graphql.document', query.query_string)
41
+ if query.selected_operation_name
42
+ span.set_data('graphql.operation.name', query.selected_operation_name)
43
+ end
44
+ if query.selected_operation
45
+ span.set_data('graphql.operation.type', query.selected_operation.operation_type)
46
+ end
47
+ end
48
+ end
49
+
50
+ result
21
51
  end
22
52
  end
23
- instrument_execution("graphql.execute", "execute_query", data) { super }
24
- end
25
53
 
26
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
54
+ include MonitorTrace::Monitor::GraphQLPrefixNames
27
55
 
28
- {
29
- "lex" => "graphql.lex",
30
- "parse" => "graphql.parse",
31
- "validate" => "graphql.validate",
32
- "analyze_query" => "graphql.analyze",
33
- "analyze_multiplex" => "graphql.analyze_multiplex",
34
- "execute_multiplex" => "graphql.execute_multiplex",
35
- "execute_query_lazy" => "graphql.execute"
36
- }.each do |trace_method, platform_key|
37
- module_eval <<-RUBY, __FILE__, __LINE__
38
- def #{trace_method}(**data)
39
- instrument_execution("#{platform_key}", "#{trace_method}", data) { super }
40
- end
41
- RUBY
42
- end
43
-
44
- # rubocop:enable Development/NoEvalCop
45
-
46
- def platform_execute_field(platform_key, &block)
47
- instrument_execution(platform_key, "execute_field", &block)
48
- end
49
-
50
- def platform_execute_field_lazy(platform_key, &block)
51
- instrument_execution(platform_key, "execute_field_lazy", &block)
52
- end
53
-
54
- def platform_authorized(platform_key, &block)
55
- instrument_execution(platform_key, "authorized", &block)
56
- end
57
-
58
- def platform_authorized_lazy(platform_key, &block)
59
- instrument_execution(platform_key, "authorized_lazy", &block)
60
- end
61
-
62
- def platform_resolve_type(platform_key, &block)
63
- instrument_execution(platform_key, "resolve_type", &block)
64
- end
56
+ private
65
57
 
66
- def platform_resolve_type_lazy(platform_key, &block)
67
- instrument_execution(platform_key, "resolve_type_lazy", &block)
68
- end
69
-
70
- def platform_field_key(field)
71
- "graphql.field.#{field.path}"
72
- end
73
-
74
- def platform_authorized_key(type)
75
- "graphql.authorized.#{type.graphql_name}"
76
- end
77
-
78
- def platform_resolve_type_key(type)
79
- "graphql.resolve_type.#{type.graphql_name}"
80
- end
81
-
82
- private
83
-
84
- def instrument_execution(platform_key, trace_method, data=nil, &block)
85
- return yield unless Sentry.initialized?
86
-
87
- Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
88
- result = yield
89
- return result unless span
90
-
91
- span.finish
92
- if trace_method == "execute_multiplex" && data.key?(:multiplex)
93
- operation_names = data[:multiplex].queries.map{|q| operation_name(q) }
94
- span.set_description(operation_names.join(", "))
95
- elsif trace_method == "execute_query" && data.key?(:query)
96
- span.set_description(operation_name(data[:query]))
97
- span.set_data('graphql.document', data[:query].query_string)
98
- span.set_data('graphql.operation.name', data[:query].selected_operation_name) if data[:query].selected_operation_name
99
- span.set_data('graphql.operation.type', data[:query].selected_operation.operation_type)
58
+ def operation_name(query)
59
+ selected_op = query.selected_operation
60
+ if selected_op
61
+ [selected_op.operation_type, selected_op.name].compact.join(' ')
62
+ else
63
+ 'GraphQL Operation'
100
64
  end
101
-
102
- result
103
65
  end
104
- end
105
66
 
106
- def operation_name(query)
107
- selected_op = query.selected_operation
108
- if selected_op
109
- [selected_op.operation_type, selected_op.name].compact.join(' ')
110
- else
111
- 'GraphQL Operation'
67
+ class Event < MonitorTrace::Monitor::Event
68
+ def start
69
+ if Sentry.initialized? && (@span = Sentry.get_current_scope.get_span)
70
+ span_name = @monitor.name_for(@keyword, @object)
71
+ @span.start_child(op: span_name)
72
+ end
73
+ end
74
+
75
+ def finish
76
+ @span&.finish
77
+ end
112
78
  end
113
79
  end
114
80
  end
@@ -1,59 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/monitor_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
7
+ # A tracer for reporting GraphQL-Ruby times to Statsd.
8
+ # Passing any Statsd client that implements `.time(name) { ... }`
9
+ # and `.timing(name, ms)` will work.
10
+ #
11
+ # @example Installing this tracer
12
+ # # eg:
13
+ # # $statsd = Statsd.new 'localhost', 9125
14
+ # class MySchema < GraphQL::Schema
15
+ # use GraphQL::Tracing::StatsdTrace, statsd: $statsd
16
+ # end
17
+ StatsdTrace = MonitorTrace.create_module("statsd")
5
18
  module StatsdTrace
6
- include PlatformTrace
7
-
8
- # @param statsd [Object] A statsd client
9
- def initialize(statsd:, **rest)
10
- @statsd = statsd
11
- super(**rest)
12
- end
19
+ class StatsdMonitor < MonitorTrace::Monitor
20
+ def initialize(statsd:, **_rest)
21
+ @statsd = statsd
22
+ super
23
+ end
13
24
 
14
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
25
+ attr_reader :statsd
15
26
 
16
- {
17
- 'lex' => "graphql.lex",
18
- 'parse' => "graphql.parse",
19
- 'validate' => "graphql.validate",
20
- 'analyze_query' => "graphql.analyze_query",
21
- 'analyze_multiplex' => "graphql.analyze_multiplex",
22
- 'execute_multiplex' => "graphql.execute_multiplex",
23
- 'execute_query' => "graphql.execute_query",
24
- 'execute_query_lazy' => "graphql.execute_query_lazy",
25
- }.each do |trace_method, platform_key|
26
- module_eval <<-RUBY, __FILE__, __LINE__
27
- def #{trace_method}(**data)
28
- @statsd.time("#{platform_key}") do
29
- super
30
- end
27
+ def instrument(keyword, object)
28
+ @statsd.time(name_for(keyword, object)) do
29
+ yield
31
30
  end
32
- RUBY
33
- end
34
-
35
- # rubocop:enable Development/NoEvalCop
36
-
37
- def platform_execute_field(platform_key, &block)
38
- @statsd.time(platform_key, &block)
39
- end
31
+ end
40
32
 
41
- def platform_authorized(key, &block)
42
- @statsd.time(key, &block)
43
- end
44
-
45
- alias :platform_resolve_type :platform_authorized
46
-
47
- def platform_field_key(field)
48
- "graphql.#{field.path}"
49
- end
33
+ include MonitorTrace::Monitor::GraphQLPrefixNames
50
34
 
51
- def platform_authorized_key(type)
52
- "graphql.authorized.#{type.graphql_name}"
53
- end
35
+ class Event < MonitorTrace::Monitor::Event
36
+ def start
37
+ @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
38
+ end
54
39
 
55
- def platform_resolve_type_key(type)
56
- "graphql.resolve_type.#{type.graphql_name}"
40
+ def finish
41
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start_time
42
+ @monitor.statsd.timing(@monitor.name_for(keyword, object), elapsed)
43
+ end
44
+ end
57
45
  end
58
46
  end
59
47
  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 StatsdTracing < PlatformTracing