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
@@ -39,6 +39,9 @@ module GraphQL
39
39
  include(BaseKeyCache)
40
40
  }
41
41
  child_class.const_set(:KeyCache, key_methods_class)
42
+
43
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
44
+
42
45
  [:execute_field, :execute_field_lazy].each do |field_trace_method|
43
46
  if !child_class.method_defined?(field_trace_method)
44
47
  child_class.module_eval <<-RUBY, __FILE__, __LINE__
@@ -91,6 +94,8 @@ module GraphQL
91
94
  end
92
95
  RUBY
93
96
  end
97
+
98
+ # rubocop:enable Development/NoEvalCop
94
99
  end
95
100
  end
96
101
 
@@ -81,10 +81,12 @@ module GraphQL
81
81
  trace_name = tracing_name.sub("Tracing", "Trace")
82
82
  if GraphQL::Tracing.const_defined?(trace_name, false)
83
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")
84
85
  schema_defn.trace_with(trace_module, **options)
85
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!")
86
88
  tracer = self.new(**options)
87
- schema_defn.tracer(tracer)
89
+ schema_defn.tracer(tracer, silence_deprecation_warning: true)
88
90
  end
89
91
  end
90
92
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
- class PrometheusTracing < PlatformTracing
7
+ module PrometheusTrace
6
8
  class GraphQLCollector < ::PrometheusExporter::Server::TypeCollector
7
9
  def initialize
8
10
  @graphql_gauge = PrometheusExporter::Metric::Base.default_aggregation.new(
@@ -28,5 +30,7 @@ module GraphQL
28
30
  end
29
31
  end
30
32
  end
33
+ # Backwards-compat:
34
+ PrometheusTracing::GraphQLCollector = PrometheusTrace::GraphQLCollector
31
35
  end
32
36
  end
@@ -1,87 +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
- {
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, &block)
28
- instrument_execution("#{platform_key}", "#{trace_method}", &block)
29
- end
30
- RUBY
31
- end
32
-
33
- def platform_execute_field(platform_key, &block)
34
- instrument_execution(platform_key, "execute_field", &block)
35
- end
36
-
37
- def platform_execute_field_lazy(platform_key, &block)
38
- instrument_execution(platform_key, "execute_field_lazy", &block)
31
+ if defined?(PrometheusExporter::Server)
32
+ autoload :GraphQLCollector, "graphql/tracing/prometheus_trace/graphql_collector"
39
33
  end
40
34
 
41
- def platform_authorized(platform_key, &block)
42
- instrument_execution(platform_key, "authorized", &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
43
41
  end
44
42
 
45
- def platform_authorized_lazy(platform_key, &block)
46
- instrument_execution(platform_key, "authorized_lazy", &block)
47
- end
43
+ attr_reader :prometheus_collector_type, :prometheus_client, :prometheus_keys_whitelist
48
44
 
49
- def platform_resolve_type(platform_key, &block)
50
- instrument_execution(platform_key, "resolve_type", &block)
51
- end
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
52
57
 
53
- def platform_resolve_type_lazy(platform_key, &block)
54
- instrument_execution(platform_key, "resolve_type_lazy", &block)
55
- end
58
+ def active?(keyword)
59
+ @trace.prometheus_keys_whitelist.include?(keyword)
60
+ end
56
61
 
57
- def platform_field_key(field)
58
- field.path
59
- end
62
+ def gettime
63
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
64
+ end
60
65
 
61
- def platform_authorized_key(type)
62
- "#{type.graphql_name}.authorized"
63
- 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
64
75
 
65
- def platform_resolve_type_key(type)
66
- "#{type.graphql_name}.resolve_type"
67
- end
76
+ include MonitorTrace::Monitor::GraphQLPrefixNames
68
77
 
69
- private
78
+ class Event < MonitorTrace::Monitor::Event
79
+ def start
80
+ @start_time = @monitor.gettime
81
+ end
70
82
 
71
- def instrument_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
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
85
89
  end
86
90
  end
87
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,71 +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
- {
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
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))
39
22
  end
40
- }
23
+ end
41
24
 
42
- self.class.instrument("GraphQL", "#{platform_key}", INSTRUMENT_OPTS) do
43
- super
25
+ ScoutApm::Tracer.instrument("GraphQL", name_for(keyword, object), INSTRUMENT_OPTS) do
26
+ yield
44
27
  end
45
28
  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
29
 
57
- alias :platform_resolve_type :platform_authorized
30
+ INSTRUMENT_OPTS = { scope: true }
58
31
 
59
- def platform_field_key(field)
60
- field.path
61
- end
32
+ include MonitorTrace::Monitor::GraphQLSuffixNames
62
33
 
63
- def platform_authorized_key(type)
64
- "#{type.graphql_name}.authorized"
65
- 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
66
41
 
67
- def platform_resolve_type_key(type)
68
- "#{type.graphql_name}.resolve_type"
42
+ def finish
43
+ @scout_req.stop_layer
44
+ end
45
+ end
69
46
  end
70
47
  end
71
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
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/tracing/monitor_trace"
4
+
5
+ module GraphQL
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")
15
+ module SentryTrace
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
51
+ end
52
+ end
53
+
54
+ include MonitorTrace::Monitor::GraphQLPrefixNames
55
+
56
+ private
57
+
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'
64
+ end
65
+ end
66
+
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
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,55 +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
19
+ class StatsdMonitor < MonitorTrace::Monitor
20
+ def initialize(statsd:, **_rest)
21
+ @statsd = statsd
22
+ super
23
+ end
7
24
 
8
- # @param statsd [Object] A statsd client
9
- def initialize(statsd:, **rest)
10
- @statsd = statsd
11
- super(**rest)
12
- end
25
+ attr_reader :statsd
13
26
 
14
- {
15
- 'lex' => "graphql.lex",
16
- 'parse' => "graphql.parse",
17
- 'validate' => "graphql.validate",
18
- 'analyze_query' => "graphql.analyze_query",
19
- 'analyze_multiplex' => "graphql.analyze_multiplex",
20
- 'execute_multiplex' => "graphql.execute_multiplex",
21
- 'execute_query' => "graphql.execute_query",
22
- 'execute_query_lazy' => "graphql.execute_query_lazy",
23
- }.each do |trace_method, platform_key|
24
- module_eval <<-RUBY, __FILE__, __LINE__
25
- def #{trace_method}(**data)
26
- @statsd.time("#{platform_key}") do
27
- super
28
- end
27
+ def instrument(keyword, object)
28
+ @statsd.time(name_for(keyword, object)) do
29
+ yield
29
30
  end
30
- RUBY
31
- end
32
-
33
- def platform_execute_field(platform_key, &block)
34
- @statsd.time(platform_key, &block)
35
- end
36
-
37
- def platform_authorized(key, &block)
38
- @statsd.time(key, &block)
39
- end
40
-
41
- alias :platform_resolve_type :platform_authorized
31
+ end
42
32
 
43
- def platform_field_key(field)
44
- "graphql.#{field.path}"
45
- end
33
+ include MonitorTrace::Monitor::GraphQLPrefixNames
46
34
 
47
- def platform_authorized_key(type)
48
- "graphql.authorized.#{type.graphql_name}"
49
- end
35
+ class Event < MonitorTrace::Monitor::Event
36
+ def start
37
+ @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
38
+ end
50
39
 
51
- def platform_resolve_type_key(type)
52
- "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
53
45
  end
54
46
  end
55
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
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require "graphql/tracing"
4
+
2
5
  module GraphQL
3
6
  module Tracing
4
7
  # This is the base class for a `trace` instance whose methods are called during query execution.
5
8
  # "Trace modes" are subclasses of this with custom tracing modules mixed in.
6
9
  #
7
10
  # A trace module may implement any of the methods on `Trace`, being sure to call `super`
8
- # to continue any tracing hooks and call the actual runtime behavior. See {GraphQL::Backtrace::Trace} for example.
11
+ # to continue any tracing hooks and call the actual runtime behavior.
9
12
  #
10
13
  class Trace
11
14
  # @param multiplex [GraphQL::Execution::Multiplex, nil]
@@ -15,10 +18,13 @@ module GraphQL
15
18
  @query = query
16
19
  end
17
20
 
21
+ # The Ruby parser doesn't call this method (`graphql/c_parser` does.)
18
22
  def lex(query_string:)
19
23
  yield
20
24
  end
21
25
 
26
+ # @param query_string [String]
27
+ # @return [void]
22
28
  def parse(query_string:)
23
29
  yield
24
30
  end
@@ -27,6 +33,22 @@ module GraphQL
27
33
  yield
28
34
  end
29
35
 
36
+ def begin_validate(query, validate)
37
+ end
38
+
39
+ def end_validate(query, validate, errors)
40
+ end
41
+
42
+ # @param multiplex [GraphQL::Execution::Multiplex]
43
+ # @param analyzers [Array<Class>]
44
+ # @return [void]
45
+ def begin_analyze_multiplex(multiplex, analyzers); end
46
+ # @param multiplex [GraphQL::Execution::Multiplex]
47
+ # @param analyzers [Array<Class>]
48
+ # @return [void]
49
+ def end_analyze_multiplex(multiplex, analyzers); end
50
+ # @param multiplex [GraphQL::Execution::Multiplex]
51
+ # @return [void]
30
52
  def analyze_multiplex(multiplex:)
31
53
  yield
32
54
  end
@@ -35,6 +57,9 @@ module GraphQL
35
57
  yield
36
58
  end
37
59
 
60
+ # This wraps an entire `.execute` call.
61
+ # @param multiplex [GraphQL::Execution::Multiplex]
62
+ # @return [void]
38
63
  def execute_multiplex(multiplex:)
39
64
  yield
40
65
  end
@@ -47,6 +72,20 @@ module GraphQL
47
72
  yield
48
73
  end
49
74
 
75
+ # GraphQL is about to resolve this field
76
+ # @param field [GraphQL::Schema::Field]
77
+ # @param object [GraphQL::Schema::Object]
78
+ # @param arguments [Hash]
79
+ # @param query [GraphQL::Query]
80
+ def begin_execute_field(field, object, arguments, query); end
81
+ # GraphQL just finished resolving this field
82
+ # @param field [GraphQL::Schema::Field]
83
+ # @param object [GraphQL::Schema::Object]
84
+ # @param arguments [Hash]
85
+ # @param query [GraphQL::Query]
86
+ # @param result [Object]
87
+ def end_execute_field(field, object, arguments, query, result); end
88
+
50
89
  def execute_field(field:, query:, ast_node:, arguments:, object:)
51
90
  yield
52
91
  end
@@ -59,6 +98,22 @@ module GraphQL
59
98
  yield
60
99
  end
61
100
 
101
+ # A call to `.authorized?` is starting
102
+ # @param type [Class<GraphQL::Schema::Object>]
103
+ # @param object [Object]
104
+ # @param context [GraphQL::Query::Context]
105
+ # @return [void]
106
+ def begin_authorized(type, object, context)
107
+ end
108
+ # A call to `.authorized?` just finished
109
+ # @param type [Class<GraphQL::Schema::Object>]
110
+ # @param object [Object]
111
+ # @param context [GraphQL::Query::Context]
112
+ # @param authorized_result [Boolean]
113
+ # @return [void]
114
+ def end_authorized(type, object, context, authorized_result)
115
+ end
116
+
62
117
  def authorized_lazy(query:, type:, object:)
63
118
  yield
64
119
  end
@@ -70,6 +125,62 @@ module GraphQL
70
125
  def resolve_type_lazy(query:, type:, object:)
71
126
  yield
72
127
  end
128
+
129
+ # A call to `.resolve_type` is starting
130
+ # @param type [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
131
+ # @param value [Object]
132
+ # @param context [GraphQL::Query::Context]
133
+ # @return [void]
134
+ def begin_resolve_type(type, value, context)
135
+ end
136
+
137
+ # A call to `.resolve_type` just ended
138
+ # @param type [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
139
+ # @param value [Object]
140
+ # @param context [GraphQL::Query::Context]
141
+ # @param resolved_type [Class<GraphQL::Schema::Object>]
142
+ # @return [void]
143
+ def end_resolve_type(type, value, context, resolved_type)
144
+ end
145
+
146
+ # A dataloader run is starting
147
+ # @param dataloader [GraphQL::Dataloader]
148
+ # @return [void]
149
+ def begin_dataloader(dataloader); end
150
+ # A dataloader run has ended
151
+ # @param dataloder [GraphQL::Dataloader]
152
+ # @return [void]
153
+ def end_dataloader(dataloader); end
154
+
155
+ # A source with pending keys is about to fetch
156
+ # @param source [GraphQL::Dataloader::Source]
157
+ # @return [void]
158
+ def begin_dataloader_source(source); end
159
+ # A fetch call has just ended
160
+ # @param source [GraphQL::Dataloader::Source]
161
+ # @return [void]
162
+ def end_dataloader_source(source); end
163
+
164
+ # Called when Dataloader spins up a new fiber for GraphQL execution
165
+ # @param jobs [Array<#call>] Execution steps to run
166
+ # @return [void]
167
+ def dataloader_spawn_execution_fiber(jobs); end
168
+ # Called when Dataloader spins up a new fiber for fetching data
169
+ # @param pending_sources [GraphQL::Dataloader::Source] Instances with pending keys
170
+ # @return [void]
171
+ def dataloader_spawn_source_fiber(pending_sources); end
172
+ # Called when an execution or source fiber terminates
173
+ # @return [void]
174
+ def dataloader_fiber_exit; end
175
+
176
+ # Called when a Dataloader fiber is paused to wait for data
177
+ # @param source [GraphQL::Dataloader::Source] The Source whose `load` call initiated this `yield`
178
+ # @return [void]
179
+ def dataloader_fiber_yield(source); end
180
+ # Called when a Dataloader fiber is resumed because data has been loaded
181
+ # @param source [GraphQL::Dataloader::Source] The Source whose `load` call previously caused this Fiber to wait
182
+ # @return [void]
183
+ def dataloader_fiber_resume(source); end
73
184
  end
74
185
  end
75
186
  end