graphql 2.0.31 → 2.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  4. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  5. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  6. data/lib/generators/graphql/install_generator.rb +49 -0
  7. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  8. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  9. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  10. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  11. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  12. data/lib/generators/graphql/templates/base_field.erb +2 -0
  13. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  15. data/lib/generators/graphql/templates/base_object.erb +2 -0
  16. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  17. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  18. data/lib/generators/graphql/templates/base_union.erb +2 -0
  19. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +5 -0
  26. data/lib/generators/graphql/type_generator.rb +1 -1
  27. data/lib/graphql/analysis/analyzer.rb +90 -0
  28. data/lib/graphql/analysis/field_usage.rb +82 -0
  29. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  30. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  31. data/lib/graphql/analysis/query_complexity.rb +263 -0
  32. data/lib/graphql/analysis/query_depth.rb +58 -0
  33. data/lib/graphql/analysis/visitor.rb +280 -0
  34. data/lib/graphql/analysis.rb +102 -1
  35. data/lib/graphql/autoload.rb +38 -0
  36. data/lib/graphql/backtrace/table.rb +118 -55
  37. data/lib/graphql/backtrace.rb +1 -19
  38. data/lib/graphql/coercion_error.rb +1 -9
  39. data/lib/graphql/current.rb +57 -0
  40. data/lib/graphql/dashboard/application_controller.rb +41 -0
  41. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  42. data/lib/graphql/dashboard/installable.rb +22 -0
  43. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  44. data/lib/graphql/dashboard/limiters.rb +93 -0
  45. data/lib/graphql/dashboard/operation_store.rb +199 -0
  46. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  47. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  48. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  49. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  50. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  51. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  52. data/lib/graphql/dashboard/statics/icon.png +0 -0
  53. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  54. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  65. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  66. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  67. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  68. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  69. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  70. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  71. data/lib/graphql/dashboard.rb +96 -0
  72. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  73. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  74. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  75. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  76. data/lib/graphql/dataloader/request.rb +5 -0
  77. data/lib/graphql/dataloader/source.rb +35 -12
  78. data/lib/graphql/dataloader.rb +224 -149
  79. data/lib/graphql/date_encoding_error.rb +1 -1
  80. data/lib/graphql/dig.rb +2 -1
  81. data/lib/graphql/duration_encoding_error.rb +16 -0
  82. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  83. data/lib/graphql/execution/finalize.rb +217 -0
  84. data/lib/graphql/execution/input_values.rb +261 -0
  85. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  86. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  87. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  88. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  89. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  90. data/lib/graphql/execution/interpreter/runtime.rb +365 -435
  91. data/lib/graphql/execution/interpreter.rb +87 -163
  92. data/lib/graphql/execution/lazy.rb +1 -1
  93. data/lib/graphql/execution/load_argument_step.rb +64 -0
  94. data/lib/graphql/execution/lookahead.rb +105 -31
  95. data/lib/graphql/execution/multiplex.rb +7 -6
  96. data/lib/graphql/execution/next.rb +90 -0
  97. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  98. data/lib/graphql/execution/runner.rb +410 -0
  99. data/lib/graphql/execution/selections_step.rb +91 -0
  100. data/lib/graphql/execution.rb +8 -4
  101. data/lib/graphql/execution_error.rb +17 -10
  102. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  103. data/lib/graphql/introspection/directive_type.rb +7 -3
  104. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  105. data/lib/graphql/introspection/entry_points.rb +20 -6
  106. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  107. data/lib/graphql/introspection/field_type.rb +13 -5
  108. data/lib/graphql/introspection/input_value_type.rb +21 -13
  109. data/lib/graphql/introspection/schema_type.rb +8 -11
  110. data/lib/graphql/introspection/type_type.rb +64 -28
  111. data/lib/graphql/invalid_name_error.rb +1 -1
  112. data/lib/graphql/invalid_null_error.rb +26 -17
  113. data/lib/graphql/language/block_string.rb +34 -18
  114. data/lib/graphql/language/cache.rb +13 -0
  115. data/lib/graphql/language/comment.rb +18 -0
  116. data/lib/graphql/language/definition_slice.rb +1 -1
  117. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  118. data/lib/graphql/language/lexer.rb +323 -193
  119. data/lib/graphql/language/nodes.rb +139 -77
  120. data/lib/graphql/language/parser.rb +807 -1985
  121. data/lib/graphql/language/printer.rb +324 -151
  122. data/lib/graphql/language/sanitized_printer.rb +21 -23
  123. data/lib/graphql/language/static_visitor.rb +171 -0
  124. data/lib/graphql/language/visitor.rb +62 -119
  125. data/lib/graphql/language.rb +71 -1
  126. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  127. data/lib/graphql/pagination/array_connection.rb +6 -6
  128. data/lib/graphql/pagination/connection.rb +30 -1
  129. data/lib/graphql/pagination/connections.rb +32 -0
  130. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  131. data/lib/graphql/query/context/scoped_context.rb +101 -0
  132. data/lib/graphql/query/context.rb +88 -144
  133. data/lib/graphql/query/null_context.rb +15 -18
  134. data/lib/graphql/query/partial.rb +194 -0
  135. data/lib/graphql/query/validation_pipeline.rb +4 -4
  136. data/lib/graphql/query/variable_validation_error.rb +1 -1
  137. data/lib/graphql/query/variables.rb +3 -3
  138. data/lib/graphql/query.rb +135 -81
  139. data/lib/graphql/railtie.rb +16 -6
  140. data/lib/graphql/rake_task.rb +3 -12
  141. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  142. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  143. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  144. data/lib/graphql/rubocop.rb +2 -0
  145. data/lib/graphql/runtime_error.rb +6 -0
  146. data/lib/graphql/schema/addition.rb +26 -13
  147. data/lib/graphql/schema/always_visible.rb +7 -2
  148. data/lib/graphql/schema/argument.rb +78 -14
  149. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  150. data/lib/graphql/schema/build_from_definition.rb +140 -66
  151. data/lib/graphql/schema/directive/flagged.rb +4 -2
  152. data/lib/graphql/schema/directive/one_of.rb +12 -0
  153. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  154. data/lib/graphql/schema/directive.rb +78 -12
  155. data/lib/graphql/schema/enum.rb +110 -27
  156. data/lib/graphql/schema/enum_value.rb +11 -3
  157. data/lib/graphql/schema/field/connection_extension.rb +4 -51
  158. data/lib/graphql/schema/field/scope_extension.rb +19 -7
  159. data/lib/graphql/schema/field.rb +245 -119
  160. data/lib/graphql/schema/field_extension.rb +12 -9
  161. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  162. data/lib/graphql/schema/input_object.rb +123 -65
  163. data/lib/graphql/schema/interface.rb +60 -16
  164. data/lib/graphql/schema/introspection_system.rb +8 -17
  165. data/lib/graphql/schema/late_bound_type.rb +4 -0
  166. data/lib/graphql/schema/list.rb +8 -4
  167. data/lib/graphql/schema/loader.rb +3 -4
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -12
  169. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  170. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  171. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  173. data/lib/graphql/schema/member/has_directives.rb +5 -5
  174. data/lib/graphql/schema/member/has_fields.rb +121 -17
  175. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  177. data/lib/graphql/schema/member/has_validators.rb +1 -1
  178. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  179. data/lib/graphql/schema/member/scoped.rb +19 -0
  180. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  181. data/lib/graphql/schema/member/validates_input.rb +3 -3
  182. data/lib/graphql/schema/member.rb +6 -0
  183. data/lib/graphql/schema/mutation.rb +7 -0
  184. data/lib/graphql/schema/non_null.rb +1 -1
  185. data/lib/graphql/schema/object.rb +34 -8
  186. data/lib/graphql/schema/printer.rb +9 -7
  187. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  188. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  189. data/lib/graphql/schema/resolver.rb +128 -32
  190. data/lib/graphql/schema/scalar.rb +4 -9
  191. data/lib/graphql/schema/subscription.rb +63 -12
  192. data/lib/graphql/schema/timeout.rb +19 -2
  193. data/lib/graphql/schema/type_expression.rb +2 -2
  194. data/lib/graphql/schema/union.rb +2 -2
  195. data/lib/graphql/schema/unique_within_type.rb +1 -1
  196. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  197. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  198. data/lib/graphql/schema/validator.rb +3 -1
  199. data/lib/graphql/schema/visibility/migration.rb +188 -0
  200. data/lib/graphql/schema/visibility/profile.rb +464 -0
  201. data/lib/graphql/schema/visibility/visit.rb +190 -0
  202. data/lib/graphql/schema/visibility.rb +311 -0
  203. data/lib/graphql/schema/warden.rb +275 -103
  204. data/lib/graphql/schema/wrapper.rb +7 -1
  205. data/lib/graphql/schema.rb +954 -212
  206. data/lib/graphql/static_validation/all_rules.rb +3 -3
  207. data/lib/graphql/static_validation/base_visitor.rb +96 -71
  208. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  209. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +2 -2
  210. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  211. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +6 -2
  212. data/lib/graphql/static_validation/rules/directives_are_defined.rb +6 -3
  213. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  214. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +14 -3
  215. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +59 -15
  216. data/lib/graphql/static_validation/rules/fields_will_merge.rb +391 -262
  217. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  218. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +6 -6
  219. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +15 -2
  220. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  221. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  222. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  223. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  224. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  225. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +28 -8
  226. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  227. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  228. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  229. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  230. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  231. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  232. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +14 -2
  233. data/lib/graphql/static_validation/validation_context.rb +22 -6
  234. data/lib/graphql/static_validation/validator.rb +9 -1
  235. data/lib/graphql/static_validation.rb +0 -1
  236. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  237. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  238. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +45 -19
  239. data/lib/graphql/subscriptions/event.rb +22 -4
  240. data/lib/graphql/subscriptions/serialize.rb +3 -1
  241. data/lib/graphql/subscriptions.rb +56 -17
  242. data/lib/graphql/testing/helpers.rb +161 -0
  243. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  244. data/lib/graphql/testing.rb +3 -0
  245. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  246. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  247. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  248. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  249. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  250. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  251. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  252. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  253. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  254. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  255. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  256. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  257. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  258. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  259. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  260. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  261. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  262. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  263. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  264. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  265. data/lib/graphql/tracing/null_trace.rb +9 -0
  266. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  267. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  268. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  269. data/lib/graphql/tracing/platform_trace.rb +5 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  272. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  273. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  274. data/lib/graphql/tracing/scout_trace.rb +32 -55
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +118 -1
  280. data/lib/graphql/tracing.rb +31 -28
  281. data/lib/graphql/type_kinds.rb +2 -1
  282. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  283. data/lib/graphql/types/relay/connection_behaviors.rb +45 -3
  284. data/lib/graphql/types/relay/edge_behaviors.rb +19 -1
  285. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  286. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  287. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  288. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  289. data/lib/graphql/types.rb +18 -10
  290. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  291. data/lib/graphql/unauthorized_error.rb +9 -1
  292. data/lib/graphql/version.rb +1 -1
  293. data/lib/graphql.rb +69 -54
  294. data/readme.md +12 -2
  295. metadata +236 -40
  296. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  297. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  298. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  299. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  300. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  301. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  302. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  303. data/lib/graphql/analysis/ast.rb +0 -81
  304. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  305. data/lib/graphql/backtrace/trace.rb +0 -96
  306. data/lib/graphql/backtrace/tracer.rb +0 -80
  307. data/lib/graphql/deprecation.rb +0 -9
  308. data/lib/graphql/filter.rb +0 -59
  309. data/lib/graphql/language/parser.y +0 -560
  310. data/lib/graphql/language/token.rb +0 -34
  311. data/lib/graphql/schema/base_64_bp.rb +0 -26
  312. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  313. data/lib/graphql/schema/null_mask.rb +0 -11
  314. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  315. data/lib/graphql/static_validation/type_stack.rb +0 -216
  316. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -2,30 +2,64 @@
2
2
  module GraphQL
3
3
  class Subscriptions
4
4
  class DefaultSubscriptionResolveExtension < GraphQL::Schema::FieldExtension
5
- def resolve(context:, object:, arguments:)
6
- has_override_implementation = @field.resolver ||
7
- object.respond_to?(@field.resolver_method)
5
+ def resolve(context:, object: nil, objects: nil, arguments:)
6
+ if objects
7
+ has_override_implementation = @field.execution_mode != :direct_send
8
8
 
9
- if !has_override_implementation
10
- if context.query.subscription_update?
11
- object.object
9
+ if !has_override_implementation
10
+ if context.query.subscription_update?
11
+ objects
12
+ else
13
+ objects.map { |o| context.skip }
14
+ end
12
15
  else
13
- context.skip
16
+ yield(objects, arguments)
14
17
  end
15
18
  else
16
- yield(object, arguments)
19
+ has_override_implementation = @field.resolver ||
20
+ object.respond_to?(@field.resolver_method)
21
+
22
+ if !has_override_implementation
23
+ if context.query.subscription_update?
24
+ object.object
25
+ else
26
+ context.skip
27
+ end
28
+ else
29
+ yield(object, arguments)
30
+ end
31
+ end
32
+ end
33
+
34
+ def after_resolve(values: nil, value: nil, context:, objects: nil, object: nil, arguments:, **rest)
35
+ if values
36
+ values.map do |value|
37
+ self.class.write_subscription(@field, value, arguments, context)
38
+ end
39
+ else
40
+ self.class.write_subscription(@field, value, arguments, context)
17
41
  end
18
42
  end
19
43
 
20
- def after_resolve(value:, context:, object:, arguments:, **rest)
44
+ def self.write_subscription(field, value, arguments, context)
21
45
  if value.is_a?(GraphQL::ExecutionError)
22
46
  value
47
+ elsif field.resolver&.method_defined?(:subscription_written?) &&
48
+ (subscription_namespace = context.namespace(:subscriptions)) &&
49
+ (subscriptions_by_path = subscription_namespace[:subscriptions])
50
+ (subscription_instance = subscriptions_by_path[context.current_path])
51
+ # If it was already written, don't append this event to be written later
52
+ if !subscription_instance.subscription_written?
53
+ events = context.namespace(:subscriptions)[:events]
54
+ events << subscription_instance.event
55
+ end
56
+ value
23
57
  elsif (events = context.namespace(:subscriptions)[:events])
24
58
  # This is the first execution, so gather an Event
25
59
  # for the backend to register:
26
60
  event = Subscriptions::Event.new(
27
61
  name: field.name,
28
- arguments: arguments_without_field_extras(arguments: arguments),
62
+ arguments: arguments,
29
63
  context: context,
30
64
  field: field,
31
65
  )
@@ -33,7 +67,7 @@ module GraphQL
33
67
  value
34
68
  elsif context.query.subscription_topic == Subscriptions::Event.serialize(
35
69
  field.name,
36
- arguments_without_field_extras(arguments: arguments),
70
+ arguments,
37
71
  field,
38
72
  scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
39
73
  )
@@ -45,14 +79,6 @@ module GraphQL
45
79
  context.skip
46
80
  end
47
81
  end
48
-
49
- private
50
-
51
- def arguments_without_field_extras(arguments:)
52
- arguments.dup.tap do |event_args|
53
- field.extras.each { |k| event_args.delete(k) }
54
- end
55
- end
56
82
  end
57
83
  end
58
84
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
 
21
21
  def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
22
22
  @name = name
23
- @arguments = arguments
23
+ @arguments = self.class.arguments_without_field_extras(arguments: arguments, field: field)
24
24
  @context = context
25
25
  field ||= context.field
26
26
  scope_key = field.subscription_scope
@@ -37,8 +37,9 @@ module GraphQL
37
37
  end
38
38
 
39
39
  # @return [String] an identifier for this unit of subscription
40
- def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
40
+ def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
41
41
  subscription = field.resolver || GraphQL::Schema::Subscription
42
+ arguments = arguments_without_field_extras(field: field, arguments: arguments)
42
43
  normalized_args = stringify_args(field, arguments.to_h, context)
43
44
  subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
44
45
  end
@@ -60,6 +61,16 @@ module GraphQL
60
61
  end
61
62
 
62
63
  class << self
64
+ def arguments_without_field_extras(arguments:, field:)
65
+ if !field.extras.empty?
66
+ arguments = arguments.dup
67
+ field.extras.each do |extra_key|
68
+ arguments.delete(extra_key)
69
+ end
70
+ end
71
+ arguments
72
+ end
73
+
63
74
  private
64
75
 
65
76
  # This method does not support cyclic references in the Hash,
@@ -93,6 +104,7 @@ module GraphQL
93
104
 
94
105
  def stringify_args(arg_owner, args, context)
95
106
  arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
107
+
96
108
  case args
97
109
  when Hash
98
110
  next_args = {}
@@ -126,12 +138,18 @@ module GraphQL
126
138
  when GraphQL::Schema::InputObject
127
139
  stringify_args(arg_owner, args.to_h, context)
128
140
  else
129
- args
141
+ if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
142
+ # `prepare:` may have made the value something other than
143
+ # a defined value of this enum -- use _that_ in this case.
144
+ arg_owner.coerce_isolated_input(args) || args
145
+ else
146
+ args
147
+ end
130
148
  end
131
149
  end
132
150
 
133
151
  def get_arg_definition(arg_owner, arg_name, context)
134
- arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
152
+ context.types.argument(arg_owner, arg_name) || context.types.arguments(arg_owner).find { |v| v.keyword.to_s == arg_name }
135
153
  end
136
154
  end
137
155
  end
@@ -146,8 +146,10 @@ module GraphQL
146
146
  elsif obj.is_a?(Date) || obj.is_a?(Time)
147
147
  # DateTime extends Date; for TimeWithZone, call `.utc` first.
148
148
  { TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
149
- elsif obj.is_a?(OpenStruct)
149
+ elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
150
150
  { OPEN_STRUCT_KEY => dump_value(obj.to_h) }
151
+ elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
152
+ dump_value(obj.to_a)
151
153
  else
152
154
  obj
153
155
  end
@@ -2,7 +2,6 @@
2
2
  require "securerandom"
3
3
  require "graphql/subscriptions/broadcast_analyzer"
4
4
  require "graphql/subscriptions/event"
5
- require "graphql/subscriptions/instrumentation"
6
5
  require "graphql/subscriptions/serialize"
7
6
  require "graphql/subscriptions/action_cable_subscriptions"
8
7
  require "graphql/subscriptions/default_subscription_resolve_extension"
@@ -30,8 +29,6 @@ module GraphQL
30
29
  raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}"
31
30
  end
32
31
 
33
- instrumentation = Subscriptions::Instrumentation.new(schema: schema)
34
- defn.instrument(:query, instrumentation)
35
32
  options[:schema] = schema
36
33
  schema.subscriptions = self.new(**options)
37
34
  schema.add_subscription_extension_if_necessary
@@ -62,17 +59,17 @@ module GraphQL
62
59
  # @return [void]
63
60
  def trigger(event_name, args, object, scope: nil, context: {})
64
61
  # Make something as context-like as possible, even though there isn't a current query:
65
- dummy_query = GraphQL::Query.new(@schema, "", validate: false, context: context)
62
+ dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context)
66
63
  context = dummy_query.context
67
64
  event_name = event_name.to_s
68
65
 
69
66
  # Try with the verbatim input first:
70
- field = @schema.get_field(@schema.subscription, event_name, context)
67
+ field = dummy_query.types.field(@schema.subscription, event_name) # rubocop:disable Development/ContextIsPassedCop
71
68
 
72
69
  if field.nil?
73
70
  # And if it wasn't found, normalize it:
74
71
  normalized_event_name = normalize_name(event_name)
75
- field = @schema.get_field(@schema.subscription, normalized_event_name, context)
72
+ field = dummy_query.types.field(@schema.subscription, normalized_event_name) # rubocop:disable Development/ContextIsPassedCop
76
73
  if field.nil?
77
74
  raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
78
75
  end
@@ -83,7 +80,7 @@ module GraphQL
83
80
 
84
81
  # Normalize symbol-keyed args to strings, try camelizing them
85
82
  # Should this accept a real context somehow?
86
- normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext)
83
+ normalized_args = normalize_arguments(normalized_event_name, field, args, @schema.null_context)
87
84
 
88
85
  event = Subscriptions::Event.new(
89
86
  name: normalized_event_name,
@@ -125,10 +122,10 @@ module GraphQL
125
122
  variables: variables,
126
123
  root_value: object,
127
124
  }
128
-
125
+
129
126
  # merge event's and query's context together
130
127
  context.merge!(event.context) unless event.context.nil? || context.nil?
131
-
128
+
132
129
  execute_options[:validate] = validate_update?(**execute_options)
133
130
  result = @schema.execute(**execute_options)
134
131
  subscriptions_context = result.context.namespace(:subscriptions)
@@ -136,11 +133,9 @@ module GraphQL
136
133
  result = nil
137
134
  end
138
135
 
139
- unsubscribed = subscriptions_context[:unsubscribed]
140
-
141
- if unsubscribed
136
+ if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update]
142
137
  # `unsubscribe` was called, clean up on our side
143
- # TODO also send `{more: false}` to client?
138
+ # The transport should also send `{more: false}` to client
144
139
  delete_subscription(subscription_id)
145
140
  result = nil
146
141
  end
@@ -164,7 +159,14 @@ module GraphQL
164
159
  res = execute_update(subscription_id, event, object)
165
160
  if !res.nil?
166
161
  deliver(subscription_id, res)
162
+
163
+ if res.context.namespace(:subscriptions)[:unsubscribed]
164
+ # `unsubscribe` was called, clean up on our side
165
+ # The transport should also send `{more: false}` to client
166
+ delete_subscription(subscription_id)
167
+ end
167
168
  end
169
+
168
170
  end
169
171
 
170
172
  # Event `event` occurred on `object`,
@@ -229,14 +231,49 @@ module GraphQL
229
231
 
230
232
  # @return [Boolean] if true, then a query like this one would be broadcasted
231
233
  def broadcastable?(query_str, **query_options)
232
- query = GraphQL::Query.new(@schema, query_str, **query_options)
234
+ query = @schema.query_class.new(@schema, query_str, **query_options)
233
235
  if !query.valid?
234
236
  raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
235
237
  end
236
- GraphQL::Analysis::AST.analyze_query(query, @schema.query_analyzers)
238
+ GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
237
239
  query.context.namespace(:subscriptions)[:subscription_broadcastable]
238
240
  end
239
241
 
242
+ # Called during execution when a new `subscription ...` operation is received
243
+ # @param query [GraphQL::Query]
244
+ # @return [void]
245
+ def initialize_subscriptions(query)
246
+ subs_namespace = query.context.namespace(:subscriptions)
247
+ subs_namespace[:events] = []
248
+ subs_namespace[:subscriptions] = {}
249
+ nil
250
+ end
251
+
252
+ # Called during execution when a subscription operation has finished
253
+ # @param query [GraphQL::Query]
254
+ # @return [void]
255
+ def finish_subscriptions(query)
256
+ if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
257
+ write_subscription(query, events)
258
+ end
259
+ nil
260
+ end
261
+
262
+ def finalizer
263
+ Finalizer.new(self)
264
+ end
265
+
266
+ class Finalizer
267
+ include Execution::Finalizer
268
+ def initialize(subscriptions)
269
+ @subscriptions = subscriptions
270
+ end
271
+
272
+ def finalize_graphql_result(query, result_data, result_key)
273
+ @subscriptions.finish_subscriptions(query)
274
+ end
275
+ end
276
+
240
277
  private
241
278
 
242
279
  # Recursively normalize `args` as belonging to `arg_owner`:
@@ -248,6 +285,8 @@ module GraphQL
248
285
  def normalize_arguments(event_name, arg_owner, args, context)
249
286
  case arg_owner
250
287
  when GraphQL::Schema::Field, Class
288
+ return args if args.nil?
289
+
251
290
  if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
252
291
  # it's a type, but not an input object
253
292
  return args
@@ -287,7 +326,7 @@ module GraphQL
287
326
  end
288
327
  end
289
328
 
290
- if missing_arg_names.any?
329
+ if !missing_arg_names.empty?
291
330
  arg_owner_name = if arg_owner.is_a?(GraphQL::Schema::Field)
292
331
  arg_owner.path
293
332
  elsif arg_owner.is_a?(Class)
@@ -300,7 +339,7 @@ module GraphQL
300
339
 
301
340
  normalized_args
302
341
  when GraphQL::Schema::List
303
- args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
342
+ args&.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
304
343
  when GraphQL::Schema::NonNull
305
344
  normalize_arguments(event_name, arg_owner.of_type, args, context)
306
345
  else
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Testing
4
+ module Helpers
5
+ # @param schema_class [Class<GraphQL::Schema>]
6
+ # @return [Module] A helpers module which always uses the given schema
7
+ def self.for(schema_class)
8
+ SchemaHelpers.for(schema_class)
9
+ end
10
+
11
+ class Error < GraphQL::Error
12
+ end
13
+
14
+ class TypeNotVisibleError < Error
15
+ def initialize(type_name:)
16
+ message = "`#{type_name}` should be `visible?` this field resolution and `context`, but it was not"
17
+ super(message)
18
+ end
19
+ end
20
+
21
+ class FieldNotVisibleError < Error
22
+ def initialize(type_name:, field_name:)
23
+ message = "`#{type_name}.#{field_name}` should be `visible?` for this resolution, but it was not"
24
+ super(message)
25
+ end
26
+ end
27
+
28
+ class TypeNotDefinedError < Error
29
+ def initialize(type_name:)
30
+ message = "No type named `#{type_name}` is defined; choose another type name or define this type."
31
+ super(message)
32
+ end
33
+ end
34
+
35
+ class FieldNotDefinedError < Error
36
+ def initialize(type_name:, field_name:)
37
+ message = "`#{type_name}` has no field named `#{field_name}`; pick another name or define this field."
38
+ super(message)
39
+ end
40
+ end
41
+
42
+ def run_graphql_field(schema, field_path, object, arguments: {}, context: {}, ast_node: nil, lookahead: nil, visibility_profile: nil)
43
+ type_name, *field_names = field_path.split(".")
44
+ dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context, visibility_profile: visibility_profile)
45
+ query_context = dummy_query.context
46
+ dataloader = query_context.dataloader
47
+ object_type = dummy_query.types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
48
+ if object_type
49
+ graphql_result = object
50
+ field_names.each do |field_name|
51
+ inner_object = graphql_result
52
+ dataloader.run_isolated {
53
+ graphql_result = object_type.wrap(inner_object, query_context)
54
+ }
55
+ if graphql_result.nil?
56
+ return nil
57
+ end
58
+ visible_field = dummy_query.types.field(object_type, field_name) # rubocop:disable Development/ContextIsPassedCop
59
+ if visible_field
60
+ dataloader.run_isolated {
61
+ query_context[:current_field] = visible_field
62
+ field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context)
63
+ field_args = schema.sync_lazy(field_args)
64
+ if !visible_field.extras.empty?
65
+ extra_args = {}
66
+ visible_field.extras.each do |extra|
67
+ extra_args[extra] = case extra
68
+ when :ast_node
69
+ ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
70
+ when :lookahead
71
+ lookahead ||= begin
72
+ ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
73
+ Execution::Lookahead.new(
74
+ query: dummy_query,
75
+ ast_nodes: [ast_node],
76
+ field: visible_field,
77
+ )
78
+ end
79
+ else
80
+ raise ArgumentError, "This extra isn't supported in `run_graphql_field` yet: `#{extra.inspect}`. Open an issue on GitHub to request it: https://github.com/rmosolgo/graphql-ruby/issues/new"
81
+ end
82
+ end
83
+
84
+ field_args = field_args.merge_extras(extra_args)
85
+ end
86
+ graphql_result = visible_field.resolve(graphql_result, field_args.keyword_arguments, query_context)
87
+ graphql_result = schema.sync_lazy(graphql_result)
88
+ }
89
+ object_type = visible_field.type.unwrap
90
+ elsif object_type.all_field_definitions.any? { |f| f.graphql_name == field_name }
91
+ raise FieldNotVisibleError.new(field_name: field_name, type_name: type_name)
92
+ else
93
+ raise FieldNotDefinedError.new(type_name: type_name, field_name: field_name)
94
+ end
95
+ end
96
+ graphql_result
97
+ else
98
+ unfiltered_type = schema.use_visibility_profile? ? schema.visibility.get_type(type_name) : schema.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
99
+ if unfiltered_type
100
+ raise TypeNotVisibleError.new(type_name: type_name)
101
+ else
102
+ raise TypeNotDefinedError.new(type_name: type_name)
103
+ end
104
+ end
105
+ end
106
+
107
+ def with_resolution_context(schema, type:, object:, context:{}, visibility_profile: nil)
108
+ resolution_context = ResolutionAssertionContext.new(
109
+ self,
110
+ schema: schema,
111
+ type_name: type,
112
+ object: object,
113
+ context: context,
114
+ visibility_profile: visibility_profile,
115
+ )
116
+ yield(resolution_context)
117
+ end
118
+
119
+ class ResolutionAssertionContext
120
+ def initialize(test, type_name:, object:, schema:, context:, visibility_profile:)
121
+ @test = test
122
+ @type_name = type_name
123
+ @object = object
124
+ @schema = schema
125
+ @context = context
126
+ @visibility_profile = visibility_profile
127
+ end
128
+
129
+ attr_reader :visibility_profile
130
+
131
+ def run_graphql_field(field_name, arguments: {})
132
+ if @schema
133
+ @test.run_graphql_field(@schema, "#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context, visibility_profile: @visibility_profile)
134
+ else
135
+ @test.run_graphql_field("#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context, visibility_profile: @visibility_profile)
136
+ end
137
+ end
138
+ end
139
+
140
+ module SchemaHelpers
141
+ include Helpers
142
+
143
+ def run_graphql_field(field_path, object, arguments: {}, context: {}, visibility_profile: nil)
144
+ super(@@schema_class_for_helpers, field_path, object, arguments: arguments, context: context, visibility_profile: visibility_profile)
145
+ end
146
+
147
+ def with_resolution_context(*args, **kwargs, &block)
148
+ # schema will be added later
149
+ super(nil, *args, **kwargs, &block)
150
+ end
151
+
152
+ def self.for(schema_class)
153
+ Module.new do
154
+ include SchemaHelpers
155
+ @@schema_class_for_helpers = schema_class
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Testing
4
+ # A stub implementation of ActionCable.
5
+ # Any methods to support the mock backend have `mock` in the name.
6
+ #
7
+ # @example Configuring your schema to use MockActionCable in the test environment
8
+ # class MySchema < GraphQL::Schema
9
+ # # Use MockActionCable in test:
10
+ # use GraphQL::Subscriptions::ActionCableSubscriptions,
11
+ # action_cable: Rails.env.test? ? GraphQL::Testing::MockActionCable : ActionCable
12
+ # end
13
+ #
14
+ # @example Clearing old data before each test
15
+ # setup do
16
+ # GraphQL::Testing::MockActionCable.clear_mocks
17
+ # end
18
+ #
19
+ # @example Using MockActionCable in a test case
20
+ # # Create a channel to use in the test, pass it to GraphQL
21
+ # mock_channel = GraphQL::Testing::MockActionCable.get_mock_channel
22
+ # ActionCableTestSchema.execute("subscription { newsFlash { text } }", context: { channel: mock_channel })
23
+ #
24
+ # # Trigger a subscription update
25
+ # ActionCableTestSchema.subscriptions.trigger(:news_flash, {}, {text: "After yesterday's rain, someone stopped on Rio Road to help a box turtle across five lanes of traffic"})
26
+ #
27
+ # # Check messages on the channel
28
+ # expected_msg = {
29
+ # result: {
30
+ # "data" => {
31
+ # "newsFlash" => {
32
+ # "text" => "After yesterday's rain, someone stopped on Rio Road to help a box turtle across five lanes of traffic"
33
+ # }
34
+ # }
35
+ # },
36
+ # more: true,
37
+ # }
38
+ # assert_equal [expected_msg], mock_channel.mock_broadcasted_messages
39
+ #
40
+ class MockActionCable
41
+ class MockChannel
42
+ def initialize
43
+ @mock_broadcasted_messages = []
44
+ end
45
+
46
+ # @return [Array<Hash>] Payloads "sent" to this channel by GraphQL-Ruby
47
+ attr_reader :mock_broadcasted_messages
48
+
49
+ # Called by ActionCableSubscriptions. Implements a Rails API.
50
+ def stream_from(stream_name, coder: nil, &block)
51
+ # Rails uses `coder`, we don't
52
+ block ||= ->(msg) { @mock_broadcasted_messages << msg }
53
+ MockActionCable.mock_stream_for(stream_name).add_mock_channel(self, block)
54
+ end
55
+ end
56
+
57
+ # Used by mock code
58
+ # @api private
59
+ class MockStream
60
+ def initialize
61
+ @mock_channels = {}
62
+ end
63
+
64
+ def add_mock_channel(channel, handler)
65
+ @mock_channels[channel] = handler
66
+ end
67
+
68
+ def mock_broadcast(message)
69
+ @mock_channels.each do |channel, handler|
70
+ handler && handler.call(message)
71
+ end
72
+ end
73
+ end
74
+
75
+ class << self
76
+ # Call this before each test run to make sure that MockActionCable's data is empty
77
+ def clear_mocks
78
+ @mock_streams = {}
79
+ end
80
+
81
+ # Implements Rails API
82
+ def server
83
+ self
84
+ end
85
+
86
+ # Implements Rails API
87
+ def broadcast(stream_name, message)
88
+ stream = @mock_streams[stream_name]
89
+ stream && stream.mock_broadcast(message)
90
+ end
91
+
92
+ # Used by mock code
93
+ def mock_stream_for(stream_name)
94
+ @mock_streams[stream_name] ||= MockStream.new
95
+ end
96
+
97
+ # Use this as `context[:channel]` to simulate an ActionCable channel
98
+ #
99
+ # @return [GraphQL::Testing::MockActionCable::MockChannel]
100
+ def get_mock_channel
101
+ MockChannel.new
102
+ end
103
+
104
+ # @return [Array<String>] Streams that currently have subscribers
105
+ def mock_stream_names
106
+ @mock_streams.keys
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/testing/helpers"
3
+ require "graphql/testing/mock_action_cable"
@@ -1,11 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql/tracing/notifications_trace'
3
+ require "graphql/tracing/notifications_trace"
4
4
 
5
5
  module GraphQL
6
6
  module Tracing
7
- # This implementation forwards events to ActiveSupport::Notifications
8
- # with a `graphql` suffix.
7
+ # This implementation forwards events to ActiveSupport::Notifications with a `graphql` suffix.
8
+ #
9
+ # @example Sending execution events to ActiveSupport::Notifications
10
+ # class MySchema < GraphQL::Schema
11
+ # trace_with(GraphQL::Tracing::ActiveSupportNotificationsTrace)
12
+ # end
13
+ #
14
+ # @example Subscribing to GraphQL events with ActiveSupport::Notifications
15
+ # ActiveSupport::Notifications.subscribe(/graphql/) do |event|
16
+ # pp event.name
17
+ # pp event.payload
18
+ # end
19
+ #
9
20
  module ActiveSupportNotificationsTrace
10
21
  include NotificationsTrace
11
22
  def initialize(engine: ActiveSupport::Notifications, **rest)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql/tracing/notifications_tracing'
3
+ require "graphql/tracing/notifications_tracing"
4
4
 
5
5
  module GraphQL
6
6
  module Tracing