graphql 2.5.9 → 2.5.26

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  4. data/lib/graphql/analysis.rb +20 -13
  5. data/lib/graphql/dashboard/application_controller.rb +41 -0
  6. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  7. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  8. data/lib/graphql/dashboard/subscriptions.rb +2 -1
  9. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
  10. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
  11. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
  12. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
  13. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
  14. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
  15. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
  16. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
  17. data/lib/graphql/dashboard.rb +11 -73
  18. data/lib/graphql/dataloader/active_record_association_source.rb +14 -2
  19. data/lib/graphql/dataloader/async_dataloader.rb +22 -11
  20. data/lib/graphql/dataloader/null_dataloader.rb +54 -9
  21. data/lib/graphql/dataloader.rb +75 -23
  22. data/lib/graphql/date_encoding_error.rb +1 -1
  23. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  24. data/lib/graphql/execution/finalize.rb +217 -0
  25. data/lib/graphql/execution/input_values.rb +261 -0
  26. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  27. data/lib/graphql/execution/interpreter/resolve.rb +10 -16
  28. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
  29. data/lib/graphql/execution/interpreter/runtime.rb +28 -33
  30. data/lib/graphql/execution/interpreter.rb +8 -22
  31. data/lib/graphql/execution/lazy.rb +1 -1
  32. data/lib/graphql/execution/load_argument_step.rb +64 -0
  33. data/lib/graphql/execution/multiplex.rb +1 -1
  34. data/lib/graphql/execution/next.rb +90 -0
  35. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  36. data/lib/graphql/execution/runner.rb +410 -0
  37. data/lib/graphql/execution/selections_step.rb +91 -0
  38. data/lib/graphql/execution.rb +8 -4
  39. data/lib/graphql/execution_error.rb +17 -10
  40. data/lib/graphql/introspection/directive_type.rb +7 -3
  41. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  42. data/lib/graphql/introspection/entry_points.rb +11 -3
  43. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  44. data/lib/graphql/introspection/field_type.rb +13 -5
  45. data/lib/graphql/introspection/input_value_type.rb +21 -13
  46. data/lib/graphql/introspection/type_type.rb +64 -28
  47. data/lib/graphql/invalid_null_error.rb +11 -5
  48. data/lib/graphql/language/document_from_schema_definition.rb +2 -1
  49. data/lib/graphql/language/lexer.rb +20 -9
  50. data/lib/graphql/language/nodes.rb +5 -1
  51. data/lib/graphql/language/parser.rb +1 -0
  52. data/lib/graphql/language.rb +21 -12
  53. data/lib/graphql/pagination/connection.rb +2 -0
  54. data/lib/graphql/pagination/connections.rb +32 -0
  55. data/lib/graphql/query/context.rb +11 -4
  56. data/lib/graphql/query/null_context.rb +9 -3
  57. data/lib/graphql/query/partial.rb +18 -3
  58. data/lib/graphql/query.rb +10 -1
  59. data/lib/graphql/runtime_error.rb +6 -0
  60. data/lib/graphql/schema/addition.rb +3 -1
  61. data/lib/graphql/schema/argument.rb +17 -0
  62. data/lib/graphql/schema/build_from_definition.rb +15 -2
  63. data/lib/graphql/schema/directive.rb +45 -13
  64. data/lib/graphql/schema/field/connection_extension.rb +4 -37
  65. data/lib/graphql/schema/field/scope_extension.rb +18 -13
  66. data/lib/graphql/schema/field.rb +87 -48
  67. data/lib/graphql/schema/field_extension.rb +11 -8
  68. data/lib/graphql/schema/interface.rb +26 -0
  69. data/lib/graphql/schema/list.rb +5 -1
  70. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
  71. data/lib/graphql/schema/member/has_arguments.rb +43 -14
  72. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  73. data/lib/graphql/schema/member/has_dataloader.rb +37 -0
  74. data/lib/graphql/schema/member/has_fields.rb +86 -5
  75. data/lib/graphql/schema/member/has_interfaces.rb +2 -2
  76. data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
  77. data/lib/graphql/schema/member.rb +5 -0
  78. data/lib/graphql/schema/non_null.rb +1 -1
  79. data/lib/graphql/schema/object.rb +1 -0
  80. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  81. data/lib/graphql/schema/resolver.rb +60 -1
  82. data/lib/graphql/schema/subscription.rb +0 -2
  83. data/lib/graphql/schema/validator/required_validator.rb +45 -5
  84. data/lib/graphql/schema/visibility/migration.rb +2 -2
  85. data/lib/graphql/schema/visibility/profile.rb +140 -56
  86. data/lib/graphql/schema/visibility.rb +31 -18
  87. data/lib/graphql/schema/wrapper.rb +7 -1
  88. data/lib/graphql/schema.rb +108 -32
  89. data/lib/graphql/static_validation/all_rules.rb +1 -1
  90. data/lib/graphql/static_validation/base_visitor.rb +90 -66
  91. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  92. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  93. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
  94. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
  95. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
  96. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
  97. data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
  98. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
  99. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  100. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
  102. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
  103. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
  104. data/lib/graphql/static_validation/validation_context.rb +1 -1
  105. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  106. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
  107. data/lib/graphql/subscriptions/event.rb +1 -0
  108. data/lib/graphql/subscriptions.rb +36 -1
  109. data/lib/graphql/testing/helpers.rb +12 -9
  110. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  111. data/lib/graphql/testing.rb +1 -0
  112. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  113. data/lib/graphql/tracing/detailed_trace.rb +70 -7
  114. data/lib/graphql/tracing/null_trace.rb +1 -1
  115. data/lib/graphql/tracing/perfetto_trace.rb +209 -79
  116. data/lib/graphql/tracing/sentry_trace.rb +3 -1
  117. data/lib/graphql/tracing/trace.rb +6 -0
  118. data/lib/graphql/type_kinds.rb +1 -0
  119. data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
  120. data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
  121. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  122. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  123. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  124. data/lib/graphql/unauthorized_error.rb +9 -1
  125. data/lib/graphql/version.rb +1 -1
  126. data/lib/graphql.rb +7 -3
  127. metadata +21 -3
@@ -1,18 +1,34 @@
1
1
  # frozen_string_literal: true
2
+ if defined?(ActiveRecord)
3
+ require "graphql/tracing/detailed_trace/active_record_backend"
4
+ end
2
5
  require "graphql/tracing/detailed_trace/memory_backend"
3
6
  require "graphql/tracing/detailed_trace/redis_backend"
4
7
 
5
8
  module GraphQL
6
9
  module Tracing
7
- # `DetailedTrace` can make detailed profiles for a subset of production traffic.
10
+ # `DetailedTrace` can make detailed profiles for a subset of production traffic. Install it in Rails with `rails generate graphql:detailed_trace`.
8
11
  #
9
12
  # When `MySchema.detailed_trace?(query)` returns `true`, a profiler-specific `trace_mode: ...` will be used for the query,
10
13
  # overriding the one in `context[:trace_mode]`.
11
14
  #
12
- # __Redis__: The sampler stores its results in a provided Redis database. Depending on your needs,
13
- # You can configure this database to retail all data (persistent) or to expire data according to your rules.
15
+ # By default, the detailed tracer calls `.inspect` on application objects returned from fields. You can customize
16
+ # this behavior by extending {DetailedTrace} and overriding {#inspect_object}. You can opt out of debug annotations
17
+ # entirely with `use ..., debug: false` or for a single query with `context: { detailed_trace_debug: false }`.
18
+ #
19
+ # You can store saved traces in two ways:
20
+ #
21
+ # - __ActiveRecord__: With `rails generate graphql:detailed_trace`, a new migration will be added to your app.
22
+ # That table will be used to store trace data.
23
+ #
24
+ # - __Redis__: Pass `redis: ...` to save trace data to a Redis database. Depending on your needs,
25
+ # you can configure this database to retain all data (persistent) or to expire data according to your rules.
26
+ #
14
27
  # If you need to save traces indefinitely, you can download them from Perfetto after opening them there.
15
28
  #
29
+ # @example Installing with Rails
30
+ # rails generate graphql:detailed_trace # optional: --redis
31
+ #
16
32
  # @example Adding the sampler to your schema
17
33
  # class MySchema < GraphQL::Schema
18
34
  # # Add the sampler:
@@ -27,24 +43,48 @@ module GraphQL
27
43
  # end
28
44
  #
29
45
  # @see Graphql::Dashboard GraphQL::Dashboard for viewing stored results
46
+ #
47
+ # @example Customizing debug output in traces
48
+ # class CustomDetailedTrace < GraphQL::Tracing::DetailedTrace
49
+ # def inspect_object(object)
50
+ # if object.is_a?(SomeThing)
51
+ # # handle it specially ...
52
+ # else
53
+ # super
54
+ # end
55
+ # end
56
+ # end
57
+ #
58
+ # @example disabling debug annotations completely
59
+ # use DetailedTrace, debug: false, ...
60
+ #
61
+ # @example disabling debug annotations for one query
62
+ # MySchema.execute(query_str, context: { detailed_trace_debug: false })
63
+ #
30
64
  class DetailedTrace
31
65
  # @param redis [Redis] If provided, profiles will be stored in Redis for later review
32
66
  # @param limit [Integer] A maximum number of profiles to store
33
- def self.use(schema, trace_mode: :profile_sample, memory: false, redis: nil, limit: nil)
67
+ # @param debug [Boolean] if `false`, it won't create `debug` annotations in Perfetto traces (reduces overhead)
68
+ # @param model_class [Class<ActiveRecord::Base>] Overrides {ActiveRecordBackend::GraphqlDetailedTrace} if present
69
+ def self.use(schema, trace_mode: :profile_sample, memory: false, debug: debug?, redis: nil, limit: nil, model_class: nil)
34
70
  storage = if redis
35
71
  RedisBackend.new(redis: redis, limit: limit)
36
72
  elsif memory
37
73
  MemoryBackend.new(limit: limit)
74
+ elsif defined?(ActiveRecord)
75
+ ActiveRecordBackend.new(limit: limit, model_class: model_class)
38
76
  else
39
- raise ArgumentError, "Pass `redis: ...` to store traces in Redis for later review"
77
+ raise ArgumentError, "To store traces, install ActiveRecord or provide `redis: ...`"
40
78
  end
41
- schema.detailed_trace = self.new(storage: storage, trace_mode: trace_mode)
79
+ detailed_trace = self.new(storage: storage, trace_mode: trace_mode, debug: debug)
80
+ schema.detailed_trace = detailed_trace
42
81
  schema.trace_with(PerfettoTrace, mode: trace_mode, save_profile: true)
43
82
  end
44
83
 
45
- def initialize(storage:, trace_mode:)
84
+ def initialize(storage:, trace_mode:, debug:)
46
85
  @storage = storage
47
86
  @trace_mode = trace_mode
87
+ @debug = debug
48
88
  end
49
89
 
50
90
  # @return [Symbol] The trace mode to use when {Schema.detailed_trace?} returns `true`
@@ -55,6 +95,11 @@ module GraphQL
55
95
  @storage.save_trace(operation_name, duration_ms, begin_ms, trace_data)
56
96
  end
57
97
 
98
+ # @return [Boolean]
99
+ def debug?
100
+ @debug
101
+ end
102
+
58
103
  # @param last [Integer]
59
104
  # @param before [Integer] Timestamp in milliseconds since epoch
60
105
  # @return [Enumerable<StoredTrace>]
@@ -77,6 +122,24 @@ module GraphQL
77
122
  @storage.delete_all_traces
78
123
  end
79
124
 
125
+ def inspect_object(object)
126
+ self.class.inspect_object(object)
127
+ end
128
+
129
+ def self.inspect_object(object)
130
+ if defined?(ActiveRecord::Relation) && object.is_a?(ActiveRecord::Relation)
131
+ "#{object.class}, .to_sql=#{object.to_sql.inspect}"
132
+ else
133
+ object.inspect
134
+ end
135
+ end
136
+
137
+ # Default debug setting
138
+ # @return [true]
139
+ def self.debug?
140
+ true
141
+ end
142
+
80
143
  class StoredTrace
81
144
  def initialize(id:, operation_name:, duration_ms:, begin_ms:, trace_data:)
82
145
  @id = id
@@ -4,6 +4,6 @@ require "graphql/tracing/trace"
4
4
 
5
5
  module GraphQL
6
6
  module Tracing
7
- NullTrace = Trace.new
7
+ NullTrace = Trace.new.freeze
8
8
  end
9
9
  end
@@ -60,11 +60,52 @@ module GraphQL
60
60
  DA_FETCH_KEYS_IID = 13
61
61
  DA_STR_VAL_NIL_IID = 14
62
62
 
63
+ REVERSE_DEBUG_NAME_LOOKUP = {
64
+ DA_OBJECT_IID => "object",
65
+ DA_RESULT_IID => "result",
66
+ DA_ARGUMENTS_IID => "arguments",
67
+ DA_FETCH_KEYS_IID => "fetch keys",
68
+ }
69
+
70
+ DEBUG_INSPECT_CATEGORY_IIDS = [15]
71
+ DA_DEBUG_INSPECT_CLASS_IID = 16
72
+ DEBUG_INSPECT_EVENT_NAME_IID = 17
73
+ DA_DEBUG_INSPECT_FOR_IID = 18
74
+
63
75
  # @param active_support_notifications_pattern [String, RegExp, false] A filter for `ActiveSupport::Notifications`, if it's present. Or `false` to skip subscribing.
64
76
  def initialize(active_support_notifications_pattern: nil, save_profile: false, **_rest)
65
77
  super
66
78
  @active_support_notifications_pattern = active_support_notifications_pattern
67
79
  @save_profile = save_profile
80
+
81
+ query = if @multiplex
82
+ @multiplex.queries.first
83
+ else
84
+ @query # could still be nil in some initializations
85
+ end
86
+
87
+ @detailed_trace = query&.schema&.detailed_trace || DetailedTrace
88
+ @create_debug_annotations = if (ctx = query&.context).nil? || (ctx_debug = ctx[:detailed_trace_debug]).nil?
89
+ @detailed_trace.debug?
90
+ else
91
+ ctx_debug
92
+ end
93
+
94
+ @arguments_filter = if (ctx = query&.context) && (dtf = ctx[:detailed_trace_filter])
95
+ dtf
96
+ elsif defined?(ActiveSupport::ParameterFilter)
97
+ fp = if defined?(Rails) && Rails.application && (app_config = Rails.application.config.filter_parameters).present? && !app_config.empty?
98
+ app_config
99
+ elsif ActiveSupport.respond_to?(:filter_parameters)
100
+ ActiveSupport.filter_parameters
101
+ else
102
+ EmptyObjects::EMPTY_ARRAY
103
+ end
104
+ ActiveSupport::ParameterFilter.new(fp, mask: ArgumentsFilter::FILTERED)
105
+ else
106
+ ArgumentsFilter.new
107
+ end
108
+
68
109
  Fiber[:graphql_flow_stack] = nil
69
110
  @sequence_id = object_id
70
111
  @pid = Process.pid
@@ -110,6 +151,10 @@ module GraphQL
110
151
  @objects_counter_id = :objects_counter.object_id
111
152
  @fibers_counter_id = :fibers_counter.object_id
112
153
  @fields_counter_id = :fields_counter.object_id
154
+ @counts_objects = [@objects_counter_id]
155
+ @counts_objects_and_fields = [@objects_counter_id, @fields_counter_id]
156
+ @counts_fibers = [@fibers_counter_id]
157
+ @counts_fibers_and_objects = [@fibers_counter_id, @objects_counter_id]
113
158
  @begin_validate = nil
114
159
  @begin_time = nil
115
160
  @packets = []
@@ -132,16 +177,19 @@ module GraphQL
132
177
  EventCategory.new(name: "ActiveSupport::Notifications", iid: ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS.first),
133
178
  EventCategory.new(name: "Authorized", iid: AUTHORIZED_CATEGORY_IIDS.first),
134
179
  EventCategory.new(name: "Resolve Type", iid: RESOLVE_TYPE_CATEGORY_IIDS.first),
180
+ EventCategory.new(name: "Debug Inspect", iid: DEBUG_INSPECT_CATEGORY_IIDS.first),
135
181
  ],
136
182
  debug_annotation_names: [
137
- DebugAnnotationName.new(name: "object", iid: DA_OBJECT_IID),
138
- DebugAnnotationName.new(name: "arguments", iid: DA_ARGUMENTS_IID),
139
- DebugAnnotationName.new(name: "result", iid: DA_RESULT_IID),
140
- DebugAnnotationName.new(name: "fetch keys", iid: DA_FETCH_KEYS_IID),
183
+ *REVERSE_DEBUG_NAME_LOOKUP.map { |(iid, name)| DebugAnnotationName.new(name: name, iid: iid) },
184
+ DebugAnnotationName.new(name: "inspect instance of", iid: DA_DEBUG_INSPECT_CLASS_IID),
185
+ DebugAnnotationName.new(name: "inspecting for", iid: DA_DEBUG_INSPECT_FOR_IID)
141
186
  ],
142
187
  debug_annotation_string_values: [
143
188
  InternedString.new(str: "(nil)", iid: DA_STR_VAL_NIL_IID),
144
189
  ],
190
+ event_names: [
191
+ EventName.new(name: "#{(@detailed_trace.is_a?(Class) ? @detailed_trace : @detailed_trace.class).name}#inspect_object", iid: DEBUG_INSPECT_EVENT_NAME_IID)
192
+ ],
145
193
  ),
146
194
  trusted_packet_sequence_id: @sequence_id,
147
195
  sequence_flags: 2,
@@ -180,11 +228,9 @@ module GraphQL
180
228
  @packets << trace_packet(
181
229
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
182
230
  track_uuid: fid,
183
- name: "Multiplex",
184
- debug_annotations: [
185
- payload_to_debug("query_string", multiplex.queries.map(&:sanitized_query_string).join("\n\n"))
186
- ]
187
- )
231
+ name: "Multiplex"
232
+ ) { [ payload_to_debug("query_string", multiplex.queries.map(&:sanitized_query_string).join("\n\n")) ] }
233
+
188
234
  result = super
189
235
 
190
236
  @packets << trace_packet(
@@ -207,9 +253,9 @@ module GraphQL
207
253
  packet = trace_packet(
208
254
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
209
255
  track_uuid: fid,
210
- name: query.context.current_path.join("."),
256
+ name: query.context.current_path&.join(".") || field.path,
211
257
  category_iids: FIELD_EXECUTE_CATEGORY_IIDS,
212
- extra_counter_track_uuids: [@objects_counter_id],
258
+ extra_counter_track_uuids: @counts_objects,
213
259
  extra_counter_values: [count_allocations],
214
260
  )
215
261
  @packets << packet
@@ -218,19 +264,23 @@ module GraphQL
218
264
  end
219
265
 
220
266
  def end_execute_field(field, object, arguments, query, app_result)
267
+ end_ts = ts
221
268
  start_field = fiber_flow_stack.pop
222
- start_field.track_event = dup_with(start_field.track_event, {
223
- debug_annotations: [
224
- payload_to_debug(nil, object.object, iid: DA_OBJECT_IID, intern_value: true),
225
- payload_to_debug(nil, arguments, iid: DA_ARGUMENTS_IID),
226
- payload_to_debug(nil, app_result, iid: DA_RESULT_IID, intern_value: true)
227
- ]
228
- })
269
+ if @create_debug_annotations
270
+ start_field.track_event = dup_with(start_field.track_event,{
271
+ debug_annotations: [
272
+ payload_to_debug(nil, (object.is_a?(GraphQL::Schema::Object) ? object.object : object), iid: DA_OBJECT_IID, intern_value: true),
273
+ payload_to_debug(nil, arguments, iid: DA_ARGUMENTS_IID),
274
+ payload_to_debug(nil, app_result, iid: DA_RESULT_IID, intern_value: true)
275
+ ]
276
+ })
277
+ end
229
278
 
230
279
  @packets << trace_packet(
280
+ timestamp: end_ts,
231
281
  type: TrackEvent::Type::TYPE_SLICE_END,
232
282
  track_uuid: fid,
233
- extra_counter_track_uuids: [@objects_counter_id, @fields_counter_id],
283
+ extra_counter_track_uuids: @counts_objects_and_fields,
234
284
  extra_counter_values: [count_allocations, count_fields],
235
285
  )
236
286
  super
@@ -240,22 +290,24 @@ module GraphQL
240
290
  @packets << trace_packet(
241
291
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
242
292
  track_uuid: fid,
243
- extra_counter_track_uuids: [@objects_counter_id],
293
+ extra_counter_track_uuids: @counts_objects,
244
294
  extra_counter_values: [count_allocations],
245
- name: "Analysis",
246
- debug_annotations: [
247
- payload_to_debug("analyzers_count", analyzers.size),
248
- payload_to_debug("analyzers", analyzers),
249
- ]
250
- )
295
+ name: "Analysis") {
296
+ [
297
+ payload_to_debug("analyzers_count", analyzers.size),
298
+ payload_to_debug("analyzers", analyzers),
299
+ ]
300
+ }
251
301
  super
252
302
  end
253
303
 
254
304
  def end_analyze_multiplex(m, analyzers)
305
+ end_ts = ts
255
306
  @packets << trace_packet(
307
+ timestamp: end_ts,
256
308
  type: TrackEvent::Type::TYPE_SLICE_END,
257
309
  track_uuid: fid,
258
- extra_counter_track_uuids: [@objects_counter_id],
310
+ extra_counter_track_uuids: @counts_objects,
259
311
  extra_counter_values: [count_allocations],
260
312
  )
261
313
  super
@@ -265,50 +317,57 @@ module GraphQL
265
317
  @packets << trace_packet(
266
318
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
267
319
  track_uuid: fid,
268
- extra_counter_track_uuids: [@objects_counter_id],
320
+ extra_counter_track_uuids: @counts_objects,
269
321
  extra_counter_values: [count_allocations],
270
322
  name: "Parse"
271
323
  )
272
324
  result = super
325
+ end_ts = ts
273
326
  @packets << trace_packet(
327
+ timestamp: end_ts,
274
328
  type: TrackEvent::Type::TYPE_SLICE_END,
275
329
  track_uuid: fid,
276
- extra_counter_track_uuids: [@objects_counter_id],
330
+ extra_counter_track_uuids: @counts_objects,
277
331
  extra_counter_values: [count_allocations],
278
332
  )
279
333
  result
280
334
  end
281
335
 
282
336
  def begin_validate(query, validate)
283
- @packets << @begin_validate = trace_packet(
337
+ @begin_validate = trace_packet(
284
338
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
285
339
  track_uuid: fid,
286
- extra_counter_track_uuids: [@objects_counter_id],
340
+ extra_counter_track_uuids: @counts_objects,
287
341
  extra_counter_values: [count_allocations],
288
- name: "Validate",
289
- debug_annotations: [
290
- payload_to_debug("validate?", validate),
291
- ]
292
- )
342
+ name: "Validate") {
343
+ [payload_to_debug("validate?", validate)]
344
+ }
345
+
346
+ @packets << @begin_validate
293
347
  super
294
348
  end
295
349
 
296
350
  def end_validate(query, validate, validation_errors)
351
+ end_ts = ts
297
352
  @packets << trace_packet(
353
+ timestamp: end_ts,
298
354
  type: TrackEvent::Type::TYPE_SLICE_END,
299
355
  track_uuid: fid,
300
- extra_counter_track_uuids: [@objects_counter_id],
356
+ extra_counter_track_uuids: @counts_objects,
301
357
  extra_counter_values: [count_allocations],
302
358
  )
303
- @begin_validate.track_event = dup_with(
304
- @begin_validate.track_event,
305
- {
306
- debug_annotations: [
307
- @begin_validate.track_event.debug_annotations.first,
308
- payload_to_debug("valid?", validation_errors.empty?)
309
- ]
310
- }
311
- )
359
+
360
+ if @create_debug_annotations
361
+ new_bv_track_event = dup_with(
362
+ @begin_validate.track_event, {
363
+ debug_annotations: [
364
+ @begin_validate.track_event.debug_annotations.first,
365
+ payload_to_debug("valid?", validation_errors.empty?)
366
+ ]
367
+ }
368
+ )
369
+ @begin_validate.track_event = new_bv_track_event
370
+ end
312
371
  super
313
372
  end
314
373
 
@@ -318,7 +377,7 @@ module GraphQL
318
377
  track_uuid: fid,
319
378
  name: "Create Execution Fiber",
320
379
  category_iids: DATALOADER_CATEGORY_IIDS,
321
- extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id],
380
+ extra_counter_track_uuids: @counts_fibers_and_objects,
322
381
  extra_counter_values: [count_fibers(1), count_allocations]
323
382
  )
324
383
  @packets << track_descriptor_packet(@did, fid, "Exec Fiber ##{fid}")
@@ -331,7 +390,7 @@ module GraphQL
331
390
  track_uuid: fid,
332
391
  name: "Create Source Fiber",
333
392
  category_iids: DATALOADER_CATEGORY_IIDS,
334
- extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id],
393
+ extra_counter_track_uuids: @counts_fibers_and_objects,
335
394
  extra_counter_values: [count_fibers(1), count_allocations]
336
395
  )
337
396
  @packets << track_descriptor_packet(@did, fid, "Source Fiber ##{fid}")
@@ -385,7 +444,7 @@ module GraphQL
385
444
  track_uuid: fid,
386
445
  name: "Fiber Exit",
387
446
  category_iids: DATALOADER_CATEGORY_IIDS,
388
- extra_counter_track_uuids: [@fibers_counter_id],
447
+ extra_counter_track_uuids: @counts_fibers,
389
448
  extra_counter_values: [count_fibers(-1)],
390
449
  )
391
450
  super
@@ -415,31 +474,34 @@ module GraphQL
415
474
  fds = @flow_ids[source]
416
475
  fds_copy = fds.dup
417
476
  fds.clear
477
+
418
478
  packet = trace_packet(
419
479
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
420
480
  track_uuid: fid,
421
481
  name_iid: @source_name_iids[source.class],
422
482
  category_iids: DATALOADER_CATEGORY_IIDS,
423
483
  flow_ids: fds_copy,
424
- extra_counter_track_uuids: [@objects_counter_id],
425
- extra_counter_values: [count_allocations],
426
- debug_annotations: [
427
- payload_to_debug(nil, source.pending.values, iid: DA_FETCH_KEYS_IID, intern_value: true),
428
- *(source.instance_variables - [:@pending, :@fetching, :@results, :@dataloader]).map { |iv|
429
- payload_to_debug(iv.to_s, source.instance_variable_get(iv), intern_value: true)
430
- }
431
- ]
432
- )
484
+ extra_counter_track_uuids: @counts_objects,
485
+ extra_counter_values: [count_allocations]) {
486
+ [
487
+ payload_to_debug(nil, source.pending.values, iid: DA_FETCH_KEYS_IID, intern_value: true),
488
+ *(source.instance_variables - [:@pending, :@fetching, :@results, :@dataloader]).map { |iv|
489
+ payload_to_debug(iv.to_s, source.instance_variable_get(iv), intern_value: true)
490
+ }
491
+ ]
492
+ }
433
493
  @packets << packet
434
494
  fiber_flow_stack << packet
435
495
  super
436
496
  end
437
497
 
438
498
  def end_dataloader_source(source)
499
+ end_ts = ts
439
500
  @packets << trace_packet(
501
+ timestamp: end_ts,
440
502
  type: TrackEvent::Type::TYPE_SLICE_END,
441
503
  track_uuid: fid,
442
- extra_counter_track_uuids: [@objects_counter_id],
504
+ extra_counter_track_uuids: @counts_objects,
443
505
  extra_counter_values: [count_allocations],
444
506
  )
445
507
  fiber_flow_stack.pop
@@ -451,7 +513,7 @@ module GraphQL
451
513
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
452
514
  track_uuid: fid,
453
515
  category_iids: AUTHORIZED_CATEGORY_IIDS,
454
- extra_counter_track_uuids: [@objects_counter_id],
516
+ extra_counter_track_uuids: @counts_objects,
455
517
  extra_counter_values: [count_allocations],
456
518
  name_iid: @auth_name_iids[type],
457
519
  )
@@ -461,14 +523,18 @@ module GraphQL
461
523
  end
462
524
 
463
525
  def end_authorized(type, obj, ctx, is_authorized)
526
+ end_ts = ts
464
527
  @packets << trace_packet(
528
+ timestamp: end_ts,
465
529
  type: TrackEvent::Type::TYPE_SLICE_END,
466
530
  track_uuid: fid,
467
- extra_counter_track_uuids: [@objects_counter_id],
531
+ extra_counter_track_uuids: @counts_objects,
468
532
  extra_counter_values: [count_allocations],
469
533
  )
470
534
  beg_auth = fiber_flow_stack.pop
471
- beg_auth.track_event = dup_with(beg_auth.track_event, { debug_annotations: [payload_to_debug("authorized?", is_authorized)] })
535
+ if @create_debug_annotations
536
+ beg_auth.track_event = dup_with(beg_auth.track_event, { debug_annotations: [payload_to_debug("authorized?", is_authorized)] })
537
+ end
472
538
  super
473
539
  end
474
540
 
@@ -477,7 +543,7 @@ module GraphQL
477
543
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
478
544
  track_uuid: fid,
479
545
  category_iids: RESOLVE_TYPE_CATEGORY_IIDS,
480
- extra_counter_track_uuids: [@objects_counter_id],
546
+ extra_counter_track_uuids: @counts_objects,
481
547
  extra_counter_values: [count_allocations],
482
548
  name_iid: @resolve_type_name_iids[type],
483
549
  )
@@ -487,14 +553,18 @@ module GraphQL
487
553
  end
488
554
 
489
555
  def end_resolve_type(type, value, context, resolved_type)
556
+ end_ts = ts
490
557
  @packets << trace_packet(
558
+ timestamp: end_ts,
491
559
  type: TrackEvent::Type::TYPE_SLICE_END,
492
560
  track_uuid: fid,
493
- extra_counter_track_uuids: [@objects_counter_id],
561
+ extra_counter_track_uuids: @counts_objects,
494
562
  extra_counter_values: [count_allocations],
495
563
  )
496
564
  rt_begin = fiber_flow_stack.pop
497
- rt_begin.track_event = dup_with(rt_begin.track_event, { debug_annotations: [payload_to_debug("resolved_type", resolved_type, intern_value: true)] })
565
+ if @create_debug_annotations
566
+ rt_begin.track_event = dup_with(rt_begin.track_event, { debug_annotations: [payload_to_debug("resolved_type", resolved_type, intern_value: true)] })
567
+ end
498
568
  super
499
569
  end
500
570
 
@@ -535,6 +605,22 @@ module GraphQL
535
605
  Fiber.current.object_id
536
606
  end
537
607
 
608
+ class ArgumentsFilter
609
+ # From Rails defaults
610
+ # https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt#L6-L8
611
+ SENSITIVE_KEY = /passw|token|crypt|email|_key|salt|certificate|secret|ssn|cvv|cvc|otp/i
612
+ FILTERED = "[FILTERED]"
613
+
614
+ def filter_param(key, value)
615
+ if (key.is_a?(String) && SENSITIVE_KEY.match?(key)) ||
616
+ (key.is_a?(Symbol) && SENSITIVE_KEY.match?(key.name))
617
+ FILTERED
618
+ else
619
+ value
620
+ end
621
+ end
622
+ end
623
+
538
624
  def debug_annotation(iid, value_key, value)
539
625
  if iid
540
626
  DebugAnnotation.new(name_iid: iid, value_key => value)
@@ -546,7 +632,6 @@ module GraphQL
546
632
  def payload_to_debug(k, v, iid: nil, intern_value: false)
547
633
  if iid.nil?
548
634
  iid = @interned_da_name_ids[k]
549
- k = nil
550
635
  end
551
636
  case v
552
637
  when String
@@ -578,15 +663,56 @@ module GraphQL
578
663
  when Symbol
579
664
  debug_annotation(iid, :string_value, v.inspect)
580
665
  when Array
581
- debug_annotation(iid, :array_values, v.map { |v2| payload_to_debug(nil, v2, intern_value: intern_value) }.compact)
666
+ debug_annotation(iid, :array_values, v.each_with_index.map { |v2, idx| payload_to_debug((k ? "#{k}.#{idx}" : String(idx)), v2, intern_value: intern_value) }.compact)
582
667
  when Hash
583
- debug_annotation(iid, :dict_entries, v.map { |k2, v2| payload_to_debug(k2, v2, intern_value: intern_value) }.compact)
668
+ debug_v = v.map { |k2, v2|
669
+ debug_k = case k2
670
+ when String
671
+ k2
672
+ when Symbol
673
+ k2.name
674
+ else
675
+ String(k2)
676
+ end
677
+ filtered_v2 = @arguments_filter.filter_param(debug_k, v2)
678
+ payload_to_debug(debug_k, filtered_v2, intern_value: intern_value)
679
+ }
680
+ debug_v.compact!
681
+ debug_annotation(iid, :dict_entries, debug_v)
682
+ when GraphQL::Schema::InputObject
683
+ payload_to_debug(k, v.to_h, iid: iid, intern_value: intern_value)
584
684
  else
585
- debug_str = if defined?(ActiveRecord::Relation) && v.is_a?(ActiveRecord::Relation)
586
- "#{v.class}, .to_sql=#{v.to_sql.inspect}"
587
- else
588
- v.inspect
685
+ class_name_iid = @interned_da_string_values[(v.class.name || "(anonymous)")]
686
+ da = [
687
+ debug_annotation(DA_DEBUG_INSPECT_CLASS_IID, :string_value_iid, class_name_iid),
688
+ ]
689
+ if k
690
+ k_str_value_iid = @interned_da_string_values[k]
691
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, k_str_value_iid)
692
+ elsif iid
693
+ k = REVERSE_DEBUG_NAME_LOOKUP[iid] || @interned_da_name_ids.key(iid)
694
+ if k.nil?
695
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, DA_STR_VAL_NIL_IID)
696
+ else
697
+ k_str_value_iid = @interned_da_string_values[k]
698
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, k_str_value_iid)
699
+ end
589
700
  end
701
+
702
+ @packets << trace_packet(
703
+ type: TrackEvent::Type::TYPE_SLICE_BEGIN,
704
+ track_uuid: fid,
705
+ name_iid: DEBUG_INSPECT_EVENT_NAME_IID,
706
+ category_iids: DEBUG_INSPECT_CATEGORY_IIDS,
707
+ extra_counter_track_uuids: @counts_objects,
708
+ extra_counter_values: [count_allocations],
709
+ debug_annotations: da,
710
+ )
711
+ debug_str = @detailed_trace.inspect_object(v)
712
+ @packets << trace_packet(
713
+ type: TrackEvent::Type::TYPE_SLICE_END,
714
+ track_uuid: fid,
715
+ )
590
716
  if intern_value
591
717
  str_iid = @interned_da_string_values[debug_str]
592
718
  debug_annotation(iid, :string_value_iid, str_iid)
@@ -622,10 +748,14 @@ module GraphQL
622
748
  Fiber[:graphql_flow_stack] ||= []
623
749
  end
624
750
 
625
- def trace_packet(event_attrs)
751
+ def trace_packet(timestamp: ts, **event_attrs)
752
+ if @create_debug_annotations && block_given?
753
+ event_attrs[:debug_annotations] = yield
754
+ end
755
+ track_event = TrackEvent.new(event_attrs)
626
756
  TracePacket.new(
627
- timestamp: ts,
628
- track_event: TrackEvent.new(event_attrs),
757
+ timestamp: timestamp,
758
+ track_event: track_event,
629
759
  trusted_packet_sequence_id: @sequence_id,
630
760
  sequence_flags: 2,
631
761
  interned_data: new_interned_data
@@ -690,9 +820,9 @@ module GraphQL
690
820
 
691
821
  def subscribe_to_active_support_notifications(pattern)
692
822
  @as_subscriber = ActiveSupport::Notifications.monotonic_subscribe(pattern) do |name, start, finish, id, payload|
693
- metadata = payload.map { |k, v| payload_to_debug(k, v, intern_value: true) }
694
- metadata.compact!
695
- te = if metadata.empty?
823
+ metadata = @create_debug_annotations ? payload.map { |k, v| payload_to_debug(String(k), v, intern_value: true) } : nil
824
+ metadata&.compact!
825
+ te = if metadata.nil? || metadata.empty?
696
826
  TrackEvent.new(
697
827
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
698
828
  track_uuid: fid,
@@ -721,7 +851,7 @@ module GraphQL
721
851
  type: TrackEvent::Type::TYPE_SLICE_END,
722
852
  track_uuid: fid,
723
853
  name: name,
724
- extra_counter_track_uuids: [@objects_counter_id],
854
+ extra_counter_track_uuids: @counts_objects,
725
855
  extra_counter_values: [count_allocations]
726
856
  ),
727
857
  trusted_packet_sequence_id: @sequence_id,
@@ -41,7 +41,9 @@ module GraphQL
41
41
  if query.selected_operation_name
42
42
  span.set_data('graphql.operation.name', query.selected_operation_name)
43
43
  end
44
- span.set_data('graphql.operation.type', query.selected_operation.operation_type)
44
+ if query.selected_operation
45
+ span.set_data('graphql.operation.type', query.selected_operation.operation_type)
46
+ end
45
47
  end
46
48
  end
47
49
 
@@ -98,6 +98,12 @@ module GraphQL
98
98
  yield
99
99
  end
100
100
 
101
+ def objects(type, object, context)
102
+ end
103
+
104
+ def object_loaded(argument_definition, object, context)
105
+ end
106
+
101
107
  # A call to `.authorized?` is starting
102
108
  # @param type [Class<GraphQL::Schema::Object>]
103
109
  # @param object [Object]