graphql 2.5.11 → 2.6.10

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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/field_extractor.rb +17 -1
  4. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  5. data/lib/generators/graphql/templates/schema.erb +2 -1
  6. data/lib/graphql/analysis/query_complexity.rb +31 -15
  7. data/lib/graphql/analysis.rb +20 -13
  8. data/lib/graphql/backtrace/table.rb +10 -1
  9. data/lib/graphql/current.rb +7 -1
  10. data/lib/graphql/dashboard/application_controller.rb +45 -0
  11. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  12. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  13. data/lib/graphql/dashboard/subscriptions.rb +2 -1
  14. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
  16. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
  17. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
  18. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
  19. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
  20. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
  21. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
  22. data/lib/graphql/dashboard.rb +14 -73
  23. data/lib/graphql/dataloader/async_dataloader.rb +336 -60
  24. data/lib/graphql/dataloader/null_dataloader.rb +48 -10
  25. data/lib/graphql/dataloader/source.rb +32 -19
  26. data/lib/graphql/dataloader.rb +107 -39
  27. data/lib/graphql/date_encoding_error.rb +1 -1
  28. data/lib/graphql/execution/directive_checks.rb +2 -0
  29. data/lib/graphql/execution/field_resolve_step.rb +804 -0
  30. data/lib/graphql/execution/finalize.rb +229 -0
  31. data/lib/graphql/execution/input_values.rb +333 -0
  32. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
  33. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  34. data/lib/graphql/execution/interpreter/resolve.rb +7 -13
  35. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
  36. data/lib/graphql/execution/interpreter/runtime.rb +60 -40
  37. data/lib/graphql/execution/interpreter.rb +17 -23
  38. data/lib/graphql/execution/lazy.rb +1 -1
  39. data/lib/graphql/execution/load_argument_step.rb +102 -0
  40. data/lib/graphql/execution/multiplex.rb +1 -1
  41. data/lib/graphql/execution/next.rb +98 -0
  42. data/lib/graphql/execution/prepare_object_step.rb +151 -0
  43. data/lib/graphql/execution/resolve_type_step.rb +27 -0
  44. data/lib/graphql/execution/runner.rb +457 -0
  45. data/lib/graphql/execution/selections_step.rb +95 -0
  46. data/lib/graphql/execution.rb +15 -4
  47. data/lib/graphql/execution_error.rb +17 -12
  48. data/lib/graphql/float_decoding_error.rb +13 -0
  49. data/lib/graphql/float_encoding_error.rb +28 -0
  50. data/lib/graphql/introspection/directive_type.rb +7 -3
  51. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  52. data/lib/graphql/introspection/entry_points.rb +11 -3
  53. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  54. data/lib/graphql/introspection/field_type.rb +13 -5
  55. data/lib/graphql/introspection/input_value_type.rb +21 -13
  56. data/lib/graphql/introspection/schema_type.rb +6 -2
  57. data/lib/graphql/introspection/type_type.rb +64 -28
  58. data/lib/graphql/invalid_null_error.rb +11 -5
  59. data/lib/graphql/language/block_string.rb +6 -11
  60. data/lib/graphql/language/cache.rb +84 -14
  61. data/lib/graphql/language/document_from_schema_definition.rb +2 -1
  62. data/lib/graphql/language/lexer.rb +27 -17
  63. data/lib/graphql/language/nodes.rb +2 -1
  64. data/lib/graphql/language/parser.rb +1 -1
  65. data/lib/graphql/language.rb +32 -17
  66. data/lib/graphql/pagination/connection.rb +2 -0
  67. data/lib/graphql/pagination/connections.rb +30 -0
  68. data/lib/graphql/pagination/relation_connection.rb +10 -1
  69. data/lib/graphql/query/context.rb +10 -3
  70. data/lib/graphql/query/null_context.rb +9 -3
  71. data/lib/graphql/query/partial.rb +18 -3
  72. data/lib/graphql/query/variable_validation_error.rb +16 -1
  73. data/lib/graphql/query/variables.rb +1 -1
  74. data/lib/graphql/query.rb +12 -3
  75. data/lib/graphql/railtie.rb +2 -1
  76. data/lib/graphql/runtime_error.rb +6 -0
  77. data/lib/graphql/schema/argument.rb +13 -1
  78. data/lib/graphql/schema/build_from_definition.rb +28 -1
  79. data/lib/graphql/schema/directive/feature.rb +4 -0
  80. data/lib/graphql/schema/directive/transform.rb +20 -0
  81. data/lib/graphql/schema/directive.rb +48 -13
  82. data/lib/graphql/schema/field/connection_extension.rb +4 -37
  83. data/lib/graphql/schema/field/scope_extension.rb +18 -13
  84. data/lib/graphql/schema/field.rb +79 -48
  85. data/lib/graphql/schema/field_extension.rb +11 -8
  86. data/lib/graphql/schema/has_single_input_argument.rb +24 -13
  87. data/lib/graphql/schema/input_object.rb +6 -2
  88. data/lib/graphql/schema/interface.rb +26 -0
  89. data/lib/graphql/schema/introspection_system.rb +6 -21
  90. data/lib/graphql/schema/list.rb +5 -1
  91. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
  92. data/lib/graphql/schema/member/has_arguments.rb +43 -14
  93. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  94. data/lib/graphql/schema/member/has_dataloader.rb +37 -0
  95. data/lib/graphql/schema/member/has_fields.rb +86 -5
  96. data/lib/graphql/schema/member.rb +5 -0
  97. data/lib/graphql/schema/non_null.rb +1 -1
  98. data/lib/graphql/schema/object.rb +1 -0
  99. data/lib/graphql/schema/printer.rb +1 -1
  100. data/lib/graphql/schema/ractor_shareable.rb +1 -0
  101. data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
  102. data/lib/graphql/schema/resolver.rb +76 -1
  103. data/lib/graphql/schema/subscription.rb +53 -10
  104. data/lib/graphql/schema/timeout.rb +2 -2
  105. data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
  106. data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
  107. data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
  108. data/lib/graphql/schema/validator/format_validator.rb +3 -3
  109. data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
  110. data/lib/graphql/schema/validator/length_validator.rb +6 -6
  111. data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
  112. data/lib/graphql/schema/validator/required_validator.rb +38 -5
  113. data/lib/graphql/schema/validator.rb +9 -0
  114. data/lib/graphql/schema/visibility/profile.rb +74 -53
  115. data/lib/graphql/schema/visibility/visit.rb +1 -1
  116. data/lib/graphql/schema/visibility.rb +32 -24
  117. data/lib/graphql/schema/wrapper.rb +11 -1
  118. data/lib/graphql/schema.rb +93 -27
  119. data/lib/graphql/static_validation/base_visitor.rb +90 -66
  120. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  121. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  122. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
  123. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
  124. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
  125. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
  126. data/lib/graphql/static_validation/rules/fields_will_merge.rb +351 -248
  127. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
  128. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  129. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
  130. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
  131. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
  132. data/lib/graphql/static_validation/validation_context.rb +1 -1
  133. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  134. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +40 -10
  135. data/lib/graphql/subscriptions/serialize.rb +10 -4
  136. data/lib/graphql/subscriptions.rb +36 -1
  137. data/lib/graphql/testing/helpers.rb +12 -9
  138. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  139. data/lib/graphql/testing.rb +1 -0
  140. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  141. data/lib/graphql/tracing/detailed_trace.rb +70 -7
  142. data/lib/graphql/tracing/perfetto_trace.rb +213 -81
  143. data/lib/graphql/tracing/sentry_trace.rb +3 -1
  144. data/lib/graphql/tracing/trace.rb +6 -0
  145. data/lib/graphql/types/float.rb +18 -4
  146. data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
  147. data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
  148. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  149. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  150. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  151. data/lib/graphql/unauthorized_error.rb +9 -1
  152. data/lib/graphql/version.rb +1 -1
  153. data/lib/graphql.rb +8 -2
  154. metadata +23 -59
@@ -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
@@ -60,11 +60,54 @@ 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
+ ANON_CLASS_NAME = "(anonymous)"
71
+
72
+ DEBUG_INSPECT_CATEGORY_IIDS = [15]
73
+ DA_DEBUG_INSPECT_CLASS_IID = 16
74
+ DEBUG_INSPECT_EVENT_NAME_IID = 17
75
+ DA_DEBUG_INSPECT_FOR_IID = 18
76
+
63
77
  # @param active_support_notifications_pattern [String, RegExp, false] A filter for `ActiveSupport::Notifications`, if it's present. Or `false` to skip subscribing.
64
78
  def initialize(active_support_notifications_pattern: nil, save_profile: false, **_rest)
65
79
  super
66
80
  @active_support_notifications_pattern = active_support_notifications_pattern
67
81
  @save_profile = save_profile
82
+
83
+ query = if @multiplex
84
+ @multiplex.queries.first
85
+ else
86
+ @query # could still be nil in some initializations
87
+ end
88
+
89
+ @detailed_trace = query&.schema&.detailed_trace || DetailedTrace
90
+ @create_debug_annotations = if (ctx = query&.context).nil? || (ctx_debug = ctx[:detailed_trace_debug]).nil?
91
+ @detailed_trace.debug?
92
+ else
93
+ ctx_debug
94
+ end
95
+
96
+ @arguments_filter = if (ctx = query&.context) && (dtf = ctx[:detailed_trace_filter])
97
+ dtf
98
+ elsif defined?(ActiveSupport::ParameterFilter)
99
+ fp = if defined?(Rails) && Rails.application && (app_config = Rails.application.config.filter_parameters).present? && !app_config.empty?
100
+ app_config
101
+ elsif ActiveSupport.respond_to?(:filter_parameters)
102
+ ActiveSupport.filter_parameters
103
+ else
104
+ EmptyObjects::EMPTY_ARRAY
105
+ end
106
+ ActiveSupport::ParameterFilter.new(fp, mask: ArgumentsFilter::FILTERED)
107
+ else
108
+ ArgumentsFilter.new
109
+ end
110
+
68
111
  Fiber[:graphql_flow_stack] = nil
69
112
  @sequence_id = object_id
70
113
  @pid = Process.pid
@@ -77,7 +120,7 @@ module GraphQL
77
120
  }
78
121
 
79
122
  @source_name_iids = Hash.new do |h, source_class|
80
- h[source_class] = @interned_event_name_iids[source_class.name]
123
+ h[source_class] = @interned_event_name_iids[source_class.name || ANON_CLASS_NAME]
81
124
  end.compare_by_identity
82
125
 
83
126
  @auth_name_iids = Hash.new do |h, graphql_type|
@@ -103,13 +146,17 @@ module GraphQL
103
146
  end
104
147
 
105
148
  @class_name_iids = Hash.new do |h, k|
106
- h[k] = @interned_da_string_values[k.name]
149
+ h[k] = @interned_da_string_values[k.name || ANON_CLASS_NAME]
107
150
  end.compare_by_identity
108
151
 
109
152
  @starting_objects = GC.stat(:total_allocated_objects)
110
153
  @objects_counter_id = :objects_counter.object_id
111
154
  @fibers_counter_id = :fibers_counter.object_id
112
155
  @fields_counter_id = :fields_counter.object_id
156
+ @counts_objects = [@objects_counter_id]
157
+ @counts_objects_and_fields = [@objects_counter_id, @fields_counter_id]
158
+ @counts_fibers = [@fibers_counter_id]
159
+ @counts_fibers_and_objects = [@fibers_counter_id, @objects_counter_id]
113
160
  @begin_validate = nil
114
161
  @begin_time = nil
115
162
  @packets = []
@@ -132,16 +179,19 @@ module GraphQL
132
179
  EventCategory.new(name: "ActiveSupport::Notifications", iid: ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS.first),
133
180
  EventCategory.new(name: "Authorized", iid: AUTHORIZED_CATEGORY_IIDS.first),
134
181
  EventCategory.new(name: "Resolve Type", iid: RESOLVE_TYPE_CATEGORY_IIDS.first),
182
+ EventCategory.new(name: "Debug Inspect", iid: DEBUG_INSPECT_CATEGORY_IIDS.first),
135
183
  ],
136
184
  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),
185
+ *REVERSE_DEBUG_NAME_LOOKUP.map { |(iid, name)| DebugAnnotationName.new(name: name, iid: iid) },
186
+ DebugAnnotationName.new(name: "inspect instance of", iid: DA_DEBUG_INSPECT_CLASS_IID),
187
+ DebugAnnotationName.new(name: "inspecting for", iid: DA_DEBUG_INSPECT_FOR_IID)
141
188
  ],
142
189
  debug_annotation_string_values: [
143
190
  InternedString.new(str: "(nil)", iid: DA_STR_VAL_NIL_IID),
144
191
  ],
192
+ event_names: [
193
+ EventName.new(name: "#{(@detailed_trace.is_a?(Class) ? @detailed_trace : @detailed_trace.class).name}#inspect_object", iid: DEBUG_INSPECT_EVENT_NAME_IID)
194
+ ],
145
195
  ),
146
196
  trusted_packet_sequence_id: @sequence_id,
147
197
  sequence_flags: 2,
@@ -180,11 +230,9 @@ module GraphQL
180
230
  @packets << trace_packet(
181
231
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
182
232
  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
- )
233
+ name: "Multiplex"
234
+ ) { [ payload_to_debug("query_string", multiplex.queries.map(&:sanitized_query_string).join("\n\n")) ] }
235
+
188
236
  result = super
189
237
 
190
238
  @packets << trace_packet(
@@ -207,9 +255,9 @@ module GraphQL
207
255
  packet = trace_packet(
208
256
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
209
257
  track_uuid: fid,
210
- name: query.context.current_path.join("."),
258
+ name: query.context.current_path&.join(".") || field.path,
211
259
  category_iids: FIELD_EXECUTE_CATEGORY_IIDS,
212
- extra_counter_track_uuids: [@objects_counter_id],
260
+ extra_counter_track_uuids: @counts_objects,
213
261
  extra_counter_values: [count_allocations],
214
262
  )
215
263
  @packets << packet
@@ -218,19 +266,23 @@ module GraphQL
218
266
  end
219
267
 
220
268
  def end_execute_field(field, object, arguments, query, app_result)
269
+ end_ts = ts
221
270
  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
- })
271
+ if @create_debug_annotations
272
+ start_field.track_event = dup_with(start_field.track_event,{
273
+ debug_annotations: [
274
+ payload_to_debug(nil, (object.is_a?(GraphQL::Schema::Object) ? object.object : object), iid: DA_OBJECT_IID, intern_value: true),
275
+ payload_to_debug(nil, arguments, iid: DA_ARGUMENTS_IID),
276
+ payload_to_debug(nil, app_result, iid: DA_RESULT_IID, intern_value: true)
277
+ ]
278
+ })
279
+ end
229
280
 
230
281
  @packets << trace_packet(
282
+ timestamp: end_ts,
231
283
  type: TrackEvent::Type::TYPE_SLICE_END,
232
284
  track_uuid: fid,
233
- extra_counter_track_uuids: [@objects_counter_id, @fields_counter_id],
285
+ extra_counter_track_uuids: @counts_objects_and_fields,
234
286
  extra_counter_values: [count_allocations, count_fields],
235
287
  )
236
288
  super
@@ -240,22 +292,24 @@ module GraphQL
240
292
  @packets << trace_packet(
241
293
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
242
294
  track_uuid: fid,
243
- extra_counter_track_uuids: [@objects_counter_id],
295
+ extra_counter_track_uuids: @counts_objects,
244
296
  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
- )
297
+ name: "Analysis") {
298
+ [
299
+ payload_to_debug("analyzers_count", analyzers.size),
300
+ payload_to_debug("analyzers", analyzers),
301
+ ]
302
+ }
251
303
  super
252
304
  end
253
305
 
254
306
  def end_analyze_multiplex(m, analyzers)
307
+ end_ts = ts
255
308
  @packets << trace_packet(
309
+ timestamp: end_ts,
256
310
  type: TrackEvent::Type::TYPE_SLICE_END,
257
311
  track_uuid: fid,
258
- extra_counter_track_uuids: [@objects_counter_id],
312
+ extra_counter_track_uuids: @counts_objects,
259
313
  extra_counter_values: [count_allocations],
260
314
  )
261
315
  super
@@ -265,50 +319,57 @@ module GraphQL
265
319
  @packets << trace_packet(
266
320
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
267
321
  track_uuid: fid,
268
- extra_counter_track_uuids: [@objects_counter_id],
322
+ extra_counter_track_uuids: @counts_objects,
269
323
  extra_counter_values: [count_allocations],
270
324
  name: "Parse"
271
325
  )
272
326
  result = super
327
+ end_ts = ts
273
328
  @packets << trace_packet(
329
+ timestamp: end_ts,
274
330
  type: TrackEvent::Type::TYPE_SLICE_END,
275
331
  track_uuid: fid,
276
- extra_counter_track_uuids: [@objects_counter_id],
332
+ extra_counter_track_uuids: @counts_objects,
277
333
  extra_counter_values: [count_allocations],
278
334
  )
279
335
  result
280
336
  end
281
337
 
282
338
  def begin_validate(query, validate)
283
- @packets << @begin_validate = trace_packet(
339
+ @begin_validate = trace_packet(
284
340
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
285
341
  track_uuid: fid,
286
- extra_counter_track_uuids: [@objects_counter_id],
342
+ extra_counter_track_uuids: @counts_objects,
287
343
  extra_counter_values: [count_allocations],
288
- name: "Validate",
289
- debug_annotations: [
290
- payload_to_debug("validate?", validate),
291
- ]
292
- )
344
+ name: "Validate") {
345
+ [payload_to_debug("validate?", validate)]
346
+ }
347
+
348
+ @packets << @begin_validate
293
349
  super
294
350
  end
295
351
 
296
352
  def end_validate(query, validate, validation_errors)
353
+ end_ts = ts
297
354
  @packets << trace_packet(
355
+ timestamp: end_ts,
298
356
  type: TrackEvent::Type::TYPE_SLICE_END,
299
357
  track_uuid: fid,
300
- extra_counter_track_uuids: [@objects_counter_id],
358
+ extra_counter_track_uuids: @counts_objects,
301
359
  extra_counter_values: [count_allocations],
302
360
  )
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
- )
361
+
362
+ if @create_debug_annotations
363
+ new_bv_track_event = dup_with(
364
+ @begin_validate.track_event, {
365
+ debug_annotations: [
366
+ @begin_validate.track_event.debug_annotations.first,
367
+ payload_to_debug("valid?", validation_errors.empty?)
368
+ ]
369
+ }
370
+ )
371
+ @begin_validate.track_event = new_bv_track_event
372
+ end
312
373
  super
313
374
  end
314
375
 
@@ -318,7 +379,7 @@ module GraphQL
318
379
  track_uuid: fid,
319
380
  name: "Create Execution Fiber",
320
381
  category_iids: DATALOADER_CATEGORY_IIDS,
321
- extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id],
382
+ extra_counter_track_uuids: @counts_fibers_and_objects,
322
383
  extra_counter_values: [count_fibers(1), count_allocations]
323
384
  )
324
385
  @packets << track_descriptor_packet(@did, fid, "Exec Fiber ##{fid}")
@@ -331,7 +392,7 @@ module GraphQL
331
392
  track_uuid: fid,
332
393
  name: "Create Source Fiber",
333
394
  category_iids: DATALOADER_CATEGORY_IIDS,
334
- extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id],
395
+ extra_counter_track_uuids: @counts_fibers_and_objects,
335
396
  extra_counter_values: [count_fibers(1), count_allocations]
336
397
  )
337
398
  @packets << track_descriptor_packet(@did, fid, "Source Fiber ##{fid}")
@@ -385,7 +446,7 @@ module GraphQL
385
446
  track_uuid: fid,
386
447
  name: "Fiber Exit",
387
448
  category_iids: DATALOADER_CATEGORY_IIDS,
388
- extra_counter_track_uuids: [@fibers_counter_id],
449
+ extra_counter_track_uuids: @counts_fibers,
389
450
  extra_counter_values: [count_fibers(-1)],
390
451
  )
391
452
  super
@@ -415,31 +476,34 @@ module GraphQL
415
476
  fds = @flow_ids[source]
416
477
  fds_copy = fds.dup
417
478
  fds.clear
479
+
418
480
  packet = trace_packet(
419
481
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
420
482
  track_uuid: fid,
421
483
  name_iid: @source_name_iids[source.class],
422
484
  category_iids: DATALOADER_CATEGORY_IIDS,
423
485
  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
- )
486
+ extra_counter_track_uuids: @counts_objects,
487
+ extra_counter_values: [count_allocations]) {
488
+ [
489
+ payload_to_debug(nil, source.pending.values, iid: DA_FETCH_KEYS_IID, intern_value: true),
490
+ *(source.instance_variables - [:@pending, :@fetching, :@results, :@dataloader]).map { |iv|
491
+ payload_to_debug(iv.to_s, source.instance_variable_get(iv), intern_value: true)
492
+ }
493
+ ]
494
+ }
433
495
  @packets << packet
434
496
  fiber_flow_stack << packet
435
497
  super
436
498
  end
437
499
 
438
500
  def end_dataloader_source(source)
501
+ end_ts = ts
439
502
  @packets << trace_packet(
503
+ timestamp: end_ts,
440
504
  type: TrackEvent::Type::TYPE_SLICE_END,
441
505
  track_uuid: fid,
442
- extra_counter_track_uuids: [@objects_counter_id],
506
+ extra_counter_track_uuids: @counts_objects,
443
507
  extra_counter_values: [count_allocations],
444
508
  )
445
509
  fiber_flow_stack.pop
@@ -451,7 +515,7 @@ module GraphQL
451
515
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
452
516
  track_uuid: fid,
453
517
  category_iids: AUTHORIZED_CATEGORY_IIDS,
454
- extra_counter_track_uuids: [@objects_counter_id],
518
+ extra_counter_track_uuids: @counts_objects,
455
519
  extra_counter_values: [count_allocations],
456
520
  name_iid: @auth_name_iids[type],
457
521
  )
@@ -461,14 +525,18 @@ module GraphQL
461
525
  end
462
526
 
463
527
  def end_authorized(type, obj, ctx, is_authorized)
528
+ end_ts = ts
464
529
  @packets << trace_packet(
530
+ timestamp: end_ts,
465
531
  type: TrackEvent::Type::TYPE_SLICE_END,
466
532
  track_uuid: fid,
467
- extra_counter_track_uuids: [@objects_counter_id],
533
+ extra_counter_track_uuids: @counts_objects,
468
534
  extra_counter_values: [count_allocations],
469
535
  )
470
536
  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)] })
537
+ if @create_debug_annotations
538
+ beg_auth.track_event = dup_with(beg_auth.track_event, { debug_annotations: [payload_to_debug("authorized?", is_authorized)] })
539
+ end
472
540
  super
473
541
  end
474
542
 
@@ -477,7 +545,7 @@ module GraphQL
477
545
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
478
546
  track_uuid: fid,
479
547
  category_iids: RESOLVE_TYPE_CATEGORY_IIDS,
480
- extra_counter_track_uuids: [@objects_counter_id],
548
+ extra_counter_track_uuids: @counts_objects,
481
549
  extra_counter_values: [count_allocations],
482
550
  name_iid: @resolve_type_name_iids[type],
483
551
  )
@@ -487,14 +555,18 @@ module GraphQL
487
555
  end
488
556
 
489
557
  def end_resolve_type(type, value, context, resolved_type)
558
+ end_ts = ts
490
559
  @packets << trace_packet(
560
+ timestamp: end_ts,
491
561
  type: TrackEvent::Type::TYPE_SLICE_END,
492
562
  track_uuid: fid,
493
- extra_counter_track_uuids: [@objects_counter_id],
563
+ extra_counter_track_uuids: @counts_objects,
494
564
  extra_counter_values: [count_allocations],
495
565
  )
496
566
  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)] })
567
+ if @create_debug_annotations
568
+ rt_begin.track_event = dup_with(rt_begin.track_event, { debug_annotations: [payload_to_debug("resolved_type", resolved_type, intern_value: true)] })
569
+ end
498
570
  super
499
571
  end
500
572
 
@@ -535,6 +607,22 @@ module GraphQL
535
607
  Fiber.current.object_id
536
608
  end
537
609
 
610
+ class ArgumentsFilter
611
+ # From Rails defaults
612
+ # https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt#L6-L8
613
+ SENSITIVE_KEY = /passw|token|crypt|email|_key|salt|certificate|secret|ssn|cvv|cvc|otp/i
614
+ FILTERED = "[FILTERED]"
615
+
616
+ def filter_param(key, value)
617
+ if (key.is_a?(String) && SENSITIVE_KEY.match?(key)) ||
618
+ (key.is_a?(Symbol) && SENSITIVE_KEY.match?(key.name))
619
+ FILTERED
620
+ else
621
+ value
622
+ end
623
+ end
624
+ end
625
+
538
626
  def debug_annotation(iid, value_key, value)
539
627
  if iid
540
628
  DebugAnnotation.new(name_iid: iid, value_key => value)
@@ -546,7 +634,6 @@ module GraphQL
546
634
  def payload_to_debug(k, v, iid: nil, intern_value: false)
547
635
  if iid.nil?
548
636
  iid = @interned_da_name_ids[k]
549
- k = nil
550
637
  end
551
638
  case v
552
639
  when String
@@ -578,15 +665,56 @@ module GraphQL
578
665
  when Symbol
579
666
  debug_annotation(iid, :string_value, v.inspect)
580
667
  when Array
581
- debug_annotation(iid, :array_values, v.map { |v2| payload_to_debug(nil, v2, intern_value: intern_value) }.compact)
668
+ 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
669
  when Hash
583
- debug_annotation(iid, :dict_entries, v.map { |k2, v2| payload_to_debug(k2, v2, intern_value: intern_value) }.compact)
670
+ debug_v = v.map { |k2, v2|
671
+ debug_k = case k2
672
+ when String
673
+ k2
674
+ when Symbol
675
+ k2.name
676
+ else
677
+ String(k2)
678
+ end
679
+ filtered_v2 = @arguments_filter.filter_param(debug_k, v2)
680
+ payload_to_debug(debug_k, filtered_v2, intern_value: intern_value)
681
+ }
682
+ debug_v.compact!
683
+ debug_annotation(iid, :dict_entries, debug_v)
684
+ when GraphQL::Schema::InputObject
685
+ payload_to_debug(k, v.to_h, iid: iid, intern_value: intern_value)
584
686
  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
687
+ class_name_iid = @interned_da_string_values[(v.class.name || ANON_CLASS_NAME)]
688
+ da = [
689
+ debug_annotation(DA_DEBUG_INSPECT_CLASS_IID, :string_value_iid, class_name_iid),
690
+ ]
691
+ if k
692
+ k_str_value_iid = @interned_da_string_values[k]
693
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, k_str_value_iid)
694
+ elsif iid
695
+ k = REVERSE_DEBUG_NAME_LOOKUP[iid] || @interned_da_name_ids.key(iid)
696
+ if k.nil?
697
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, DA_STR_VAL_NIL_IID)
698
+ else
699
+ k_str_value_iid = @interned_da_string_values[k]
700
+ da << debug_annotation(DA_DEBUG_INSPECT_FOR_IID, :string_value_iid, k_str_value_iid)
701
+ end
589
702
  end
703
+
704
+ @packets << trace_packet(
705
+ type: TrackEvent::Type::TYPE_SLICE_BEGIN,
706
+ track_uuid: fid,
707
+ name_iid: DEBUG_INSPECT_EVENT_NAME_IID,
708
+ category_iids: DEBUG_INSPECT_CATEGORY_IIDS,
709
+ extra_counter_track_uuids: @counts_objects,
710
+ extra_counter_values: [count_allocations],
711
+ debug_annotations: da,
712
+ )
713
+ debug_str = @detailed_trace.inspect_object(v)
714
+ @packets << trace_packet(
715
+ type: TrackEvent::Type::TYPE_SLICE_END,
716
+ track_uuid: fid,
717
+ )
590
718
  if intern_value
591
719
  str_iid = @interned_da_string_values[debug_str]
592
720
  debug_annotation(iid, :string_value_iid, str_iid)
@@ -622,10 +750,14 @@ module GraphQL
622
750
  Fiber[:graphql_flow_stack] ||= []
623
751
  end
624
752
 
625
- def trace_packet(event_attrs)
753
+ def trace_packet(timestamp: ts, **event_attrs)
754
+ if @create_debug_annotations && block_given?
755
+ event_attrs[:debug_annotations] = yield
756
+ end
757
+ track_event = TrackEvent.new(event_attrs)
626
758
  TracePacket.new(
627
- timestamp: ts,
628
- track_event: TrackEvent.new(event_attrs),
759
+ timestamp: timestamp,
760
+ track_event: track_event,
629
761
  trusted_packet_sequence_id: @sequence_id,
630
762
  sequence_flags: 2,
631
763
  interned_data: new_interned_data
@@ -690,9 +822,9 @@ module GraphQL
690
822
 
691
823
  def subscribe_to_active_support_notifications(pattern)
692
824
  @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?
825
+ metadata = @create_debug_annotations ? payload.map { |k, v| payload_to_debug(String(k), v, intern_value: true) } : nil
826
+ metadata&.compact!
827
+ te = if metadata.nil? || metadata.empty?
696
828
  TrackEvent.new(
697
829
  type: TrackEvent::Type::TYPE_SLICE_BEGIN,
698
830
  track_uuid: fid,
@@ -721,7 +853,7 @@ module GraphQL
721
853
  type: TrackEvent::Type::TYPE_SLICE_END,
722
854
  track_uuid: fid,
723
855
  name: name,
724
- extra_counter_track_uuids: [@objects_counter_id],
856
+ extra_counter_track_uuids: @counts_objects,
725
857
  extra_counter_values: [count_allocations]
726
858
  ),
727
859
  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