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
@@ -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
@@ -1,2 +1,3 @@
1
1
  # frozen_string_literal: true
2
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
 
@@ -20,10 +22,12 @@ module GraphQL
20
22
  # These GraphQL events will show up as 'graphql.execute' spans
21
23
  EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
22
24
 
25
+
23
26
  # During auto-instrumentation this version of AppOpticsTracing is compared
24
27
  # with the version provided in the appoptics_apm gem, so that the newer
25
28
  # version of the class can be used
26
29
 
30
+
27
31
  def self.version
28
32
  Gem::Version.new('1.0.0')
29
33
  end
@@ -85,7 +89,7 @@ module GraphQL
85
89
  end
86
90
  end
87
91
 
88
- def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
92
+ def execute_field_lazy(query:, field:, ast_node:, arguments:, object:) # rubocop:disable Development/TraceCallsSuperCop
89
93
  execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
90
94
  end
91
95
 
@@ -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
 
@@ -20,6 +22,11 @@ module GraphQL
20
22
  # These GraphQL events will show up as 'graphql.execute' spans
21
23
  EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
22
24
 
25
+ def initialize(...)
26
+ warn "GraphQL::Tracing::AppOptics tracing is deprecated; update to SolarWindsAPM instead, which uses OpenTelemetry."
27
+ super
28
+ end
29
+
23
30
  # During auto-instrumentation this version of AppOpticsTracing is compared
24
31
  # with the version provided in the appoptics_apm gem, so that the newer
25
32
  # version of the class can be used
@@ -1,81 +1,54 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/tracing/monitor_trace"
2
3
 
3
4
  module GraphQL
4
5
  module Tracing
6
+ # Instrumentation for reporting GraphQL-Ruby times to Appsignal.
7
+ #
8
+ # @example Installing the tracer
9
+ # class MySchema < GraphQL::Schema
10
+ # trace_with GraphQL::Tracing::AppsignalTrace
11
+ # end
12
+ AppsignalTrace = MonitorTrace.create_module("appsignal")
5
13
  module AppsignalTrace
6
- include PlatformTrace
7
-
8
14
  # @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
9
15
  # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
10
16
  # It can also be specified per-query with `context[:set_appsignal_action_name]`.
11
17
  def initialize(set_action_name: false, **rest)
12
- @set_action_name = set_action_name
18
+ rest[:set_transaction_name] ||= set_action_name
19
+ setup_appsignal_monitor(**rest)
13
20
  super
14
21
  end
15
22
 
16
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
- {
19
- "lex" => "lex.graphql",
20
- "parse" => "parse.graphql",
21
- "validate" => "validate.graphql",
22
- "analyze_query" => "analyze.graphql",
23
- "analyze_multiplex" => "analyze.graphql",
24
- "execute_multiplex" => "execute.graphql",
25
- "execute_query" => "execute.graphql",
26
- "execute_query_lazy" => "execute.graphql",
27
- }.each do |trace_method, platform_key|
28
- module_eval <<-RUBY, __FILE__, __LINE__
29
- def #{trace_method}(**data)
30
- #{
31
- if trace_method == "execute_query"
32
- <<-RUBY
33
- set_this_txn_name = data[:query].context[:set_appsignal_action_name]
34
- if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
35
- Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
36
- end
37
- RUBY
38
- end
39
- }
40
-
41
- Appsignal.instrument("#{platform_key}") do
42
- super
23
+ class AppsignalMonitor < MonitorTrace::Monitor
24
+ def instrument(keyword, object)
25
+ if keyword == :execute
26
+ query = object.queries.first
27
+ set_this_txn_name = query.context[:set_appsignal_action_name]
28
+ if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
29
+ Appsignal::Transaction.current.set_action(transaction_name(query))
43
30
  end
44
31
  end
45
- RUBY
46
- end
47
-
48
- # rubocop:enable Development/NoEvalCop
49
-
50
- def platform_execute_field(platform_key)
51
- Appsignal.instrument(platform_key) do
52
- yield
32
+ Appsignal.instrument(name_for(keyword, object)) do
33
+ yield
34
+ end
53
35
  end
54
- end
55
36
 
56
- def platform_authorized(platform_key)
57
- Appsignal.instrument(platform_key) do
58
- yield
59
- end
60
- end
37
+ include MonitorTrace::Monitor::GraphQLSuffixNames
38
+ class Event < GraphQL::Tracing::MonitorTrace::Monitor::Event
39
+ def start
40
+ Appsignal::Transaction.current.start_event
41
+ end
61
42
 
62
- def platform_resolve_type(platform_key)
63
- Appsignal.instrument(platform_key) do
64
- yield
43
+ def finish
44
+ Appsignal::Transaction.current.finish_event(
45
+ @monitor.name_for(@keyword, @object),
46
+ "",
47
+ ""
48
+ )
49
+ end
65
50
  end
66
51
  end
67
-
68
- def platform_field_key(field)
69
- "#{field.owner.graphql_name}.#{field.graphql_name}.graphql"
70
- end
71
-
72
- def platform_authorized_key(type)
73
- "#{type.graphql_name}.authorized.graphql"
74
- end
75
-
76
- def platform_resolve_type_key(type)
77
- "#{type.graphql_name}.resolve_type.graphql"
78
- end
79
52
  end
80
53
  end
81
54
  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 AppsignalTracing < PlatformTracing
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+ # This trace class calls legacy-style tracer with payload hashes.
6
+ # New-style `trace_with` modules significantly reduce the overhead of tracing,
7
+ # but that advantage is lost when legacy-style tracers are also used (since the payload hashes are still constructed).
8
+ module CallLegacyTracers
9
+ def lex(query_string:)
10
+ (@multiplex || @query).trace("lex", { query_string: query_string }) { super }
11
+ end
12
+
13
+ def parse(query_string:)
14
+ (@multiplex || @query).trace("parse", { query_string: query_string }) { super }
15
+ end
16
+
17
+ def validate(query:, validate:)
18
+ query.trace("validate", { validate: validate, query: query }) { super }
19
+ end
20
+
21
+ def analyze_multiplex(multiplex:)
22
+ multiplex.trace("analyze_multiplex", { multiplex: multiplex }) { super }
23
+ end
24
+
25
+ def analyze_query(query:)
26
+ query.trace("analyze_query", { query: query }) { super }
27
+ end
28
+
29
+ def execute_multiplex(multiplex:)
30
+ multiplex.trace("execute_multiplex", { multiplex: multiplex }) { super }
31
+ end
32
+
33
+ def execute_query(query:)
34
+ query.trace("execute_query", { query: query }) { super }
35
+ end
36
+
37
+ def execute_query_lazy(query:, multiplex:)
38
+ multiplex.trace("execute_query_lazy", { multiplex: multiplex, query: query }) { super }
39
+ end
40
+
41
+ def execute_field(field:, query:, ast_node:, arguments:, object:)
42
+ query.trace("execute_field", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
43
+ end
44
+
45
+ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
46
+ query.trace("execute_field_lazy", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super }
47
+ end
48
+
49
+ def authorized(query:, type:, object:)
50
+ query.trace("authorized", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
51
+ end
52
+
53
+ def authorized_lazy(query:, type:, object:)
54
+ query.trace("authorized_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
55
+ end
56
+
57
+ def resolve_type(query:, type:, object:)
58
+ query.trace("resolve_type", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
59
+ end
60
+
61
+ def resolve_type_lazy(query:, type:, object:)
62
+ query.trace("resolve_type_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,187 +1,71 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/tracing/monitor_trace"
2
3
 
3
4
  module GraphQL
4
5
  module Tracing
6
+ # A tracer for reporting to DataDog
7
+ # @example Adding this tracer to your schema
8
+ # class MySchema < GraphQL::Schema
9
+ # trace_with GraphQL::Tracing::DataDogTrace
10
+ # end
11
+ # @example Skipping `resolve_type` and `authorized` events
12
+ # trace_with GraphQL::Tracing::DataDogTrace, trace_authorized: false, trace_resolve_type: false
13
+ DataDogTrace = MonitorTrace.create_module("datadog")
5
14
  module DataDogTrace
6
- # @param tracer [#trace] Deprecated
7
- # @param analytics_enabled [Boolean] Deprecated
8
- # @param analytics_sample_rate [Float] Deprecated
9
- def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **rest)
10
- if tracer.nil?
11
- tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
15
+ class DatadogMonitor < MonitorTrace::Monitor
16
+ def initialize(set_transaction_name:, service: nil, tracer: nil, **_rest)
17
+ super
18
+ if tracer.nil?
19
+ tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
20
+ end
21
+ @tracer = tracer
22
+ @service_name = service
23
+ @has_prepare_span = @trace.respond_to?(:prepare_span)
12
24
  end
13
- @tracer = tracer
14
-
15
- @analytics_enabled = analytics_enabled
16
- @analytics_sample_rate = analytics_sample_rate
17
-
18
- @service_name = service
19
- @has_prepare_span = respond_to?(:prepare_span)
20
- super
21
- end
22
-
23
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
24
-
25
- {
26
- 'lex' => 'lex.graphql',
27
- 'parse' => 'parse.graphql',
28
- 'validate' => 'validate.graphql',
29
- 'analyze_query' => 'analyze.graphql',
30
- 'analyze_multiplex' => 'analyze.graphql',
31
- 'execute_multiplex' => 'execute.graphql',
32
- 'execute_query' => 'execute.graphql',
33
- 'execute_query_lazy' => 'execute.graphql',
34
- }.each do |trace_method, trace_key|
35
- module_eval <<-RUBY, __FILE__, __LINE__
36
- def #{trace_method}(**data)
37
- @tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span|
38
- span.set_tag('component', 'graphql')
39
- span.set_tag('operation', '#{trace_method}')
40
25
 
41
- #{
42
- if trace_method == 'execute_multiplex'
43
- <<-RUBY
44
- operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
26
+ attr_reader :tracer, :service_name
45
27
 
46
- resource = if operations.empty?
47
- first_query = data[:multiplex].queries.first
48
- fallback_transaction_name(first_query && first_query.context)
49
- else
50
- operations
51
- end
52
- span.resource = resource if resource
53
-
54
- # [Deprecated] will be removed in the future
55
- span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled
56
- RUBY
57
- elsif trace_method == 'execute_query'
58
- <<-RUBY
59
- span.set_tag(:selected_operation_name, data[:query].selected_operation_name)
60
- span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
61
- span.set_tag(:query_string, data[:query].query_string)
62
- RUBY
63
- end
64
- }
65
- if @has_prepare_span
66
- prepare_span("#{trace_method.sub("platform_", "")}", data, span)
28
+ def instrument(keyword, object)
29
+ trace_key = name_for(keyword, object)
30
+ @tracer.trace(trace_key, service: @service_name, type: 'custom') do |span|
31
+ span.set_tag('component', 'graphql')
32
+ op_name = keyword.respond_to?(:name) ? keyword.name : keyword.to_s
33
+ span.set_tag('operation', op_name)
34
+
35
+ if keyword == :execute
36
+ operations = object.queries.map(&:selected_operation_name).join(', ')
37
+ first_query = object.queries.first
38
+ resource = if operations.empty?
39
+ fallback_transaction_name(first_query && first_query.context)
40
+ else
41
+ operations
67
42
  end
68
- super
69
- end
70
- end
71
- RUBY
72
- end
73
-
74
- # rubocop:enable Development/NoEvalCop
43
+ span.resource = resource if resource
75
44
 
76
- def execute_field_span(span_key, query, field, ast_node, arguments, object)
77
- return_type = field.type.unwrap
78
- trace_field = if return_type.kind.scalar? || return_type.kind.enum?
79
- (field.trace.nil? && @trace_scalars) || field.trace
80
- else
81
- true
82
- end
83
- platform_key = if trace_field
84
- @platform_key_cache[DataDogTrace].platform_field_key_cache[field]
85
- else
86
- nil
87
- end
88
- if platform_key && trace_field
89
- @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
90
- span.set_tag('component', 'graphql')
91
- span.set_tag('operation', span_key)
45
+ span.set_tag("selected_operation_name", first_query.selected_operation_name)
46
+ span.set_tag("selected_operation_type", first_query.selected_operation&.operation_type)
47
+ span.set_tag("query_string", first_query.query_string)
48
+ end
92
49
 
93
50
  if @has_prepare_span
94
- prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
95
- prepare_span(span_key, prepare_span_data, span)
51
+ @trace.prepare_span(keyword, object, span)
96
52
  end
97
53
  yield
98
54
  end
99
- else
100
- yield
101
- end
102
- end
103
- def execute_field(query:, field:, ast_node:, arguments:, object:)
104
- execute_field_span("execute_field", query, field, ast_node, arguments, object) do
105
- super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
106
- end
107
- end
108
-
109
- def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
110
- execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do
111
- super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
112
- end
113
- end
114
-
115
- def authorized(query:, type:, object:)
116
- authorized_span("authorized", object, type, query) do
117
- super(query: query, type: type, object: object)
118
55
  end
119
- end
120
-
121
- def authorized_span(span_key, object, type, query)
122
- platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
123
- @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
124
- span.set_tag('component', 'graphql')
125
- span.set_tag('operation', span_key)
126
56
 
127
- if @has_prepare_span
128
- prepare_span(span_key, {object: object, type: type, query: query}, span)
57
+ include MonitorTrace::Monitor::GraphQLSuffixNames
58
+ class Event < MonitorTrace::Monitor::Event
59
+ def start
60
+ name = @monitor.name_for(keyword, object)
61
+ @dd_span = @monitor.tracer.trace(name, service: @monitor.service_name, type: 'custom')
129
62
  end
130
- yield
131
- end
132
- end
133
-
134
- def authorized_lazy(object:, type:, query:)
135
- authorized_span("authorized_lazy", object, type, query) do
136
- super(query: query, type: type, object: object)
137
- end
138
- end
139
-
140
- def resolve_type(object:, type:, query:)
141
- resolve_type_span("resolve_type", object, type, query) do
142
- super(object: object, query: query, type: type)
143
- end
144
- end
145
63
 
146
- def resolve_type_lazy(object:, type:, query:)
147
- resolve_type_span("resolve_type_lazy", object, type, query) do
148
- super(object: object, query: query, type: type)
149
- end
150
- end
151
-
152
- def resolve_type_span(span_key, object, type, query)
153
- platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
154
- @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span|
155
- span.set_tag('component', 'graphql')
156
- span.set_tag('operation', span_key)
157
-
158
- if @has_prepare_span
159
- prepare_span(span_key, {object: object, type: type, query: query}, span)
64
+ def finish
65
+ @dd_span.finish
160
66
  end
161
- yield
162
67
  end
163
68
  end
164
-
165
- include PlatformTrace
166
-
167
- # Implement this method in a subclass to apply custom tags to datadog spans
168
- # @param key [String] The event being traced
169
- # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
170
- # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
171
- # def prepare_span(key, data, span)
172
- # end
173
-
174
- def platform_field_key(field)
175
- field.path
176
- end
177
-
178
- def platform_authorized_key(type)
179
- "#{type.graphql_name}.authorized"
180
- end
181
-
182
- def platform_resolve_type_key(type)
183
- "#{type.graphql_name}.resolve_type"
184
- end
185
69
  end
186
70
  end
187
71
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_tracing"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  class DataDogTracing < PlatformTracing