deimos-ruby 1.24.2 → 2.0.0.pre.alpha1

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -17
  3. data/.tool-versions +1 -0
  4. data/CHANGELOG.md +5 -0
  5. data/README.md +287 -498
  6. data/deimos-ruby.gemspec +4 -4
  7. data/docs/CONFIGURATION.md +133 -226
  8. data/docs/UPGRADING.md +237 -0
  9. data/lib/deimos/active_record_consume/batch_consumption.rb +29 -28
  10. data/lib/deimos/active_record_consume/mass_updater.rb +59 -4
  11. data/lib/deimos/active_record_consume/message_consumption.rb +15 -21
  12. data/lib/deimos/active_record_consumer.rb +36 -21
  13. data/lib/deimos/active_record_producer.rb +28 -9
  14. data/lib/deimos/backends/base.rb +4 -35
  15. data/lib/deimos/backends/kafka.rb +6 -22
  16. data/lib/deimos/backends/kafka_async.rb +6 -22
  17. data/lib/deimos/backends/{db.rb → outbox.rb} +13 -9
  18. data/lib/deimos/config/configuration.rb +116 -379
  19. data/lib/deimos/consume/batch_consumption.rb +24 -124
  20. data/lib/deimos/consume/message_consumption.rb +36 -63
  21. data/lib/deimos/consumer.rb +16 -75
  22. data/lib/deimos/ext/consumer_route.rb +35 -0
  23. data/lib/deimos/ext/producer_middleware.rb +94 -0
  24. data/lib/deimos/ext/producer_route.rb +22 -0
  25. data/lib/deimos/ext/redraw.rb +29 -0
  26. data/lib/deimos/ext/routing_defaults.rb +72 -0
  27. data/lib/deimos/ext/schema_route.rb +70 -0
  28. data/lib/deimos/kafka_message.rb +2 -2
  29. data/lib/deimos/kafka_source.rb +2 -7
  30. data/lib/deimos/kafka_topic_info.rb +1 -1
  31. data/lib/deimos/logging.rb +71 -0
  32. data/lib/deimos/message.rb +2 -11
  33. data/lib/deimos/metrics/datadog.rb +40 -1
  34. data/lib/deimos/metrics/provider.rb +4 -4
  35. data/lib/deimos/producer.rb +39 -116
  36. data/lib/deimos/railtie.rb +6 -0
  37. data/lib/deimos/schema_backends/avro_base.rb +21 -21
  38. data/lib/deimos/schema_backends/avro_schema_registry.rb +1 -2
  39. data/lib/deimos/schema_backends/avro_validation.rb +2 -2
  40. data/lib/deimos/schema_backends/base.rb +19 -12
  41. data/lib/deimos/schema_backends/mock.rb +6 -1
  42. data/lib/deimos/schema_backends/plain.rb +47 -0
  43. data/lib/deimos/schema_class/base.rb +2 -2
  44. data/lib/deimos/schema_class/enum.rb +1 -1
  45. data/lib/deimos/schema_class/record.rb +2 -2
  46. data/lib/deimos/test_helpers.rb +95 -320
  47. data/lib/deimos/tracing/provider.rb +6 -6
  48. data/lib/deimos/transcoder.rb +88 -0
  49. data/lib/deimos/utils/db_poller/base.rb +16 -14
  50. data/lib/deimos/utils/db_poller/state_based.rb +3 -3
  51. data/lib/deimos/utils/db_poller/time_based.rb +4 -4
  52. data/lib/deimos/utils/db_poller.rb +1 -1
  53. data/lib/deimos/utils/deadlock_retry.rb +1 -1
  54. data/lib/deimos/utils/{db_producer.rb → outbox_producer.rb} +16 -47
  55. data/lib/deimos/utils/schema_class.rb +0 -7
  56. data/lib/deimos/version.rb +1 -1
  57. data/lib/deimos.rb +79 -26
  58. data/lib/generators/deimos/{db_backend_generator.rb → outbox_backend_generator.rb} +4 -4
  59. data/lib/generators/deimos/schema_class_generator.rb +0 -1
  60. data/lib/generators/deimos/v2/templates/karafka.rb.tt +149 -0
  61. data/lib/generators/deimos/v2_generator.rb +193 -0
  62. data/lib/tasks/deimos.rake +5 -7
  63. data/spec/active_record_batch_consumer_association_spec.rb +22 -13
  64. data/spec/active_record_batch_consumer_spec.rb +84 -65
  65. data/spec/active_record_consume/batch_consumption_spec.rb +10 -10
  66. data/spec/active_record_consume/batch_slicer_spec.rb +12 -12
  67. data/spec/active_record_consume/mass_updater_spec.rb +137 -0
  68. data/spec/active_record_consumer_spec.rb +29 -13
  69. data/spec/active_record_producer_spec.rb +36 -26
  70. data/spec/backends/base_spec.rb +0 -23
  71. data/spec/backends/kafka_async_spec.rb +1 -3
  72. data/spec/backends/kafka_spec.rb +1 -3
  73. data/spec/backends/{db_spec.rb → outbox_spec.rb} +14 -20
  74. data/spec/batch_consumer_spec.rb +66 -116
  75. data/spec/consumer_spec.rb +53 -147
  76. data/spec/deimos_spec.rb +10 -126
  77. data/spec/kafka_source_spec.rb +19 -52
  78. data/spec/karafka/karafka.rb +69 -0
  79. data/spec/karafka_config/karafka_spec.rb +97 -0
  80. data/spec/logging_spec.rb +25 -0
  81. data/spec/message_spec.rb +9 -9
  82. data/spec/producer_spec.rb +112 -254
  83. data/spec/rake_spec.rb +1 -3
  84. data/spec/schema_backends/avro_validation_spec.rb +1 -1
  85. data/spec/schemas/com/my-namespace/MySchemaWithTitle.avsc +22 -0
  86. data/spec/snapshots/consumers-no-nest.snap +49 -0
  87. data/spec/snapshots/consumers.snap +49 -0
  88. data/spec/snapshots/consumers_and_producers-no-nest.snap +49 -0
  89. data/spec/snapshots/consumers_and_producers.snap +49 -0
  90. data/spec/snapshots/consumers_circular-no-nest.snap +49 -0
  91. data/spec/snapshots/consumers_circular.snap +49 -0
  92. data/spec/snapshots/consumers_complex_types-no-nest.snap +49 -0
  93. data/spec/snapshots/consumers_complex_types.snap +49 -0
  94. data/spec/snapshots/consumers_nested-no-nest.snap +49 -0
  95. data/spec/snapshots/consumers_nested.snap +49 -0
  96. data/spec/snapshots/namespace_folders.snap +49 -0
  97. data/spec/snapshots/namespace_map.snap +49 -0
  98. data/spec/snapshots/producers_with_key-no-nest.snap +49 -0
  99. data/spec/snapshots/producers_with_key.snap +49 -0
  100. data/spec/spec_helper.rb +61 -29
  101. data/spec/utils/db_poller_spec.rb +49 -39
  102. data/spec/utils/{db_producer_spec.rb → outbox_producer_spec.rb} +17 -184
  103. metadata +58 -67
  104. data/lib/deimos/batch_consumer.rb +0 -7
  105. data/lib/deimos/config/phobos_config.rb +0 -163
  106. data/lib/deimos/instrumentation.rb +0 -95
  107. data/lib/deimos/monkey_patches/phobos_cli.rb +0 -35
  108. data/lib/deimos/utils/inline_consumer.rb +0 -158
  109. data/lib/deimos/utils/lag_reporter.rb +0 -186
  110. data/lib/deimos/utils/schema_controller_mixin.rb +0 -129
  111. data/spec/config/configuration_spec.rb +0 -321
  112. data/spec/kafka_listener_spec.rb +0 -55
  113. data/spec/phobos.bad_db.yml +0 -73
  114. data/spec/phobos.yml +0 -77
  115. data/spec/utils/inline_consumer_spec.rb +0 -31
  116. data/spec/utils/lag_reporter_spec.rb +0 -76
  117. data/spec/utils/platform_schema_validation_spec.rb +0 -0
  118. data/spec/utils/schema_controller_mixin_spec.rb +0 -84
  119. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/migration +0 -0
  120. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/rails3_migration +0 -0
@@ -4,6 +4,7 @@ require 'active_support/concern'
4
4
  require 'active_support/core_ext'
5
5
  require 'deimos/tracing/mock'
6
6
  require 'deimos/metrics/mock'
7
+ require 'karafka/testing/rspec/helpers'
7
8
 
8
9
  module Deimos
9
10
  # Include this module in your RSpec spec_helper
@@ -11,122 +12,79 @@ module Deimos
11
12
  # and add methods to use to test encoding/decoding.
12
13
  module TestHelpers
13
14
  extend ActiveSupport::Concern
15
+ def self.included(base)
16
+ super
17
+ base.include Karafka::Testing::RSpec::Helpers
18
+ end
19
+
20
+ # @return [Array<Hash>]
21
+ def sent_messages
22
+ self.class.sent_messages
23
+ end
14
24
 
15
25
  class << self
16
- # for backwards compatibility
17
26
  # @return [Array<Hash>]
18
27
  def sent_messages
19
- Deimos::Backends::Test.sent_messages
28
+ Karafka.producer.client.messages.map do |m|
29
+ produced_message = m.except(:label).deep_dup
30
+ Deimos.decode_message(produced_message)
31
+ produced_message[:payload] = Deimos::TestHelpers.normalize_message(produced_message[:payload])
32
+ produced_message[:key] = Deimos::TestHelpers.normalize_message(produced_message[:key])
33
+ produced_message
34
+ end
20
35
  end
21
36
 
22
37
  # Set the config to the right settings for a unit test
23
38
  # @return [void]
24
39
  def unit_test!
25
- Deimos.configure do |deimos_config|
26
- deimos_config.logger = Logger.new(STDOUT)
27
- deimos_config.consumers.reraise_errors = true
28
- deimos_config.kafka.seed_brokers ||= ['test_broker']
29
- deimos_config.schema.backend = Deimos.schema_backend_class.mock_backend
30
- deimos_config.producers.backend = :test
31
- deimos_config.tracer = Deimos::Tracing::Mock.new
32
- end
33
- end
34
-
35
- # Kafka test config with avro schema registry
36
- # @return [void]
37
- def full_integration_test!
38
- Deimos.configure do |deimos_config|
39
- deimos_config.producers.backend = :kafka
40
- deimos_config.schema.backend = :avro_schema_registry
41
- end
42
- end
43
-
44
- # Set the config to the right settings for a kafka test
45
- # @return [void]
46
- def kafka_test!
47
- Deimos.configure do |deimos_config|
48
- deimos_config.producers.backend = :kafka
49
- deimos_config.schema.backend = :avro_validation
50
- end
40
+ Deimos.config.schema.backend = :avro_validation
41
+ warn "unit_test! is deprecated and can be replaced by setting Deimos's schema backend to `:avro_validation`. All other test behavior is provided by Karafka."
51
42
  end
52
43
  end
53
44
 
54
- included do
55
-
56
- RSpec.configure do |config|
57
- config.prepend_before(:each) do
58
- client = double('client').as_null_object
59
- allow(client).to receive(:time) do |*_args, &block|
60
- block.call
61
- end
62
- Deimos::Backends::Test.sent_messages.clear
63
- end
64
- end
65
-
66
- end
67
-
68
- # @deprecated
69
- # @!visibility private
70
- def stub_producers_and_consumers!
71
- warn('stub_producers_and_consumers! is no longer necessary and this method will be removed in 3.0')
72
- end
73
-
74
- # @deprecated
75
- # @!visibility private
76
- def stub_producer(_klass)
77
- warn('Stubbing producers is no longer necessary and this method will be removed in 3.0')
78
- end
79
-
80
- # @deprecated
81
- # @!visibility private
82
- def stub_consumer(_klass)
83
- warn('Stubbing consumers is no longer necessary and this method will be removed in 3.0')
84
- end
85
-
86
- # @deprecated
87
- # @!visibility private
88
- def stub_batch_consumer(_klass)
89
- warn('Stubbing batch consumers is no longer necessary and this method will be removed in 3.0')
90
- end
91
-
92
45
  # get the difference of 2 hashes.
93
- # @param hash1 [Hash]
94
- # @param hash2 [Hash]
46
+ # @param hash1 [Hash, nil]
47
+ # @param hash2 [Hash, nil]
95
48
  # @!visibility private
96
49
  def _hash_diff(hash1, hash2)
97
- if hash1.nil? || !hash1.is_a?(Hash)
98
- hash2
99
- elsif hash2.nil? || !hash2.is_a?(Hash)
100
- hash1
50
+ h1 = Deimos::TestHelpers.normalize_message(hash1)
51
+ h2 = Deimos::TestHelpers.normalize_message(hash2)
52
+ if h1.nil? || !h1.is_a?(Hash)
53
+ h2
54
+ elsif h2.nil? || !h2.is_a?(Hash)
55
+ h1
101
56
  else
102
- hash1.dup.
103
- delete_if { |k, v| hash2[k] == v }.
104
- merge!(hash2.dup.delete_if { |k, _v| hash1.key?(k) })
57
+ h1.dup.
58
+ delete_if { |k, v| h2[k] == v }.
59
+ merge!(h2.dup.delete_if { |k, _v| h1.key?(k) })
60
+ end
61
+ end
62
+
63
+ def self.normalize_message(m)
64
+ return nil if m.nil?
65
+
66
+ if m.respond_to?(:to_h)
67
+ m = m.to_h
105
68
  end
69
+ if m.respond_to?(:with_indifferent_access)
70
+ m = m.with_indifferent_access
71
+ end
72
+ m
106
73
  end
107
74
 
108
75
  # @!visibility private
109
76
  def _frk_failure_message(topic, message, key=nil, partition_key=nil, was_negated=false)
110
- messages = Deimos::Backends::Test.sent_messages.
111
- select { |m| m[:topic] == topic }.
112
- map { |m| m.except(:topic) }
77
+ messages = Deimos::TestHelpers.sent_messages.select { |m| m[:topic] == topic }
113
78
  message_string = ''
114
79
  diff = nil
115
80
  min_hash_diff = nil
81
+ message = Deimos::TestHelpers.normalize_message(message)
116
82
  if messages.any?
117
- message_string = messages.map(&:inspect).join("\n")
118
- min_hash_diff = messages.min_by { |m| _hash_diff(m, message).keys.size }
119
- diff = RSpec::Expectations.differ.
120
- diff_as_object(message, min_hash_diff[:payload])
83
+ message_string = messages.map { |m| m[:payload].inspect}.join("\n")
84
+ min_hash_diff = messages.min_by { |m| _hash_diff(m, message)&.keys&.size }
85
+ diff = RSpec::Expectations.differ.diff_as_object(message, min_hash_diff[:payload])
121
86
  end
122
- description = if message.respond_to?(:description)
123
- message.description
124
- elsif message.nil?
125
- 'nil'
126
- else
127
- message
128
- end
129
- str = "Expected #{topic} #{'not ' if was_negated}to have sent #{description}"
87
+ str = "Expected #{topic} #{'not ' if was_negated}to have sent #{message.try(:to_h) || message}"
130
88
  str += " with key #{key}" if key
131
89
  str += " with partition key #{partition_key}" if partition_key
132
90
  str += "\nClosest message received: #{min_hash_diff}" if min_hash_diff
@@ -135,23 +93,18 @@ module Deimos
135
93
  end
136
94
 
137
95
  RSpec::Matchers.define :have_sent do |msg, key=nil, partition_key=nil, headers=nil|
138
- message = if msg.respond_to?(:with_indifferent_access)
139
- msg.with_indifferent_access
140
- else
141
- msg
142
- end
96
+ message = Deimos::TestHelpers.normalize_message(msg)
143
97
  match do |topic|
144
- Deimos::Backends::Test.sent_messages.any? do |m|
145
- hash_matcher = RSpec::Matchers::BuiltIn::Match.new(message)
146
- hash_matcher.send(:match,
147
- message&.respond_to?(:to_h) ? message.to_h : message,
148
- m[:payload]&.with_indifferent_access) &&
98
+ message_key = Deimos::TestHelpers.normalize_message(key)
99
+ hash_matcher = RSpec::Matchers::BuiltIn::Match.new(message)
100
+ Deimos::TestHelpers.sent_messages.any? do |m|
101
+ hash_matcher.send(:match, message, m[:payload]) &&
149
102
  topic == m[:topic] &&
150
- (key.present? ? key == m[:key] : true) &&
103
+ (key.present? ? message_key == m[:key] : true) &&
151
104
  (partition_key.present? ? partition_key == m[:partition_key] : true) &&
152
105
  if headers.present?
153
106
  hash_matcher.send(:match,
154
- headers&.with_indifferent_access,
107
+ headers.with_indifferent_access,
155
108
  m[:headers]&.with_indifferent_access)
156
109
  else
157
110
  true
@@ -159,20 +112,11 @@ module Deimos
159
112
  end
160
113
  end
161
114
 
162
- if respond_to?(:failure_message)
163
- failure_message do |topic|
164
- _frk_failure_message(topic, message, key, partition_key)
165
- end
166
- failure_message_when_negated do |topic|
167
- _frk_failure_message(topic, message, key, partition_key, true)
168
- end
169
- else
170
- failure_message_for_should do |topic|
171
- _frk_failure_message(topic, message, key, partition_key)
172
- end
173
- failure_message_for_should_not do |topic|
174
- _frk_failure_message(topic, message, key, partition_key, true)
175
- end
115
+ failure_message do |topic|
116
+ _frk_failure_message(topic, message, key, partition_key)
117
+ end
118
+ failure_message_when_negated do |topic|
119
+ _frk_failure_message(topic, message, key, partition_key, true)
176
120
  end
177
121
  end
178
122
 
@@ -180,7 +124,8 @@ module Deimos
180
124
  # particular messages were sent or not sent after a point in time.
181
125
  # @return [void]
182
126
  def clear_kafka_messages!
183
- Deimos::Backends::Test.sent_messages.clear
127
+ puts "[Deprecated] clear_kafka_messages! can be replaced with `karafka.produced_messages.clear`"
128
+ karafka.produced_messages.clear
184
129
  end
185
130
 
186
131
  # Test that a given handler will consume a given payload correctly, i.e.
@@ -190,65 +135,19 @@ module Deimos
190
135
  # @param handler_class_or_topic [Class, String] Class which inherits from
191
136
  # Deimos::Consumer or the topic as a string
192
137
  # @param payload [Hash] the payload to consume
193
- # @param call_original [Boolean] if true, allow the consume handler
194
- # to continue as normal. Not compatible with a block.
195
- # @param skip_expectation [Boolean] Set to true to not place any
196
- # expectations on the consumer. Primarily used internally to Deimos.
197
138
  # @param key [Object] the key to use.
139
+ # @param call_original [Symbol] legacy parameter.
198
140
  # @param partition_key [Object] the partition key to use.
199
141
  # @return [void]
200
142
  def test_consume_message(handler_class_or_topic,
201
143
  payload,
202
- call_original: false,
203
144
  key: nil,
204
- partition_key: nil,
205
- skip_expectation: false,
206
- &block)
207
- raise 'Cannot have both call_original and be given a block!' if call_original && block_given?
208
-
209
- payload.stringify_keys! if payload.respond_to?(:stringify_keys!)
210
- handler_class = if handler_class_or_topic.is_a?(String)
211
- _get_handler_class_from_topic(handler_class_or_topic)
212
- else
213
- handler_class_or_topic
214
- end
215
- handler = handler_class.new
216
- allow(handler_class).to receive(:new).and_return(handler)
217
- listener = double('listener',
218
- handler_class: handler_class,
219
- encoding: nil)
220
- key ||= _key_from_consumer(handler_class)
221
- message = double('message',
222
- 'key' => key,
223
- 'partition_key' => partition_key,
224
- 'partition' => 1,
225
- 'offset' => 1,
226
- 'headers' => {},
227
- 'value' => payload)
228
-
229
- unless skip_expectation
230
- _handler_expectation(:consume,
231
- payload,
232
- handler,
233
- call_original,
234
- &block)
145
+ call_original: nil,
146
+ partition_key: nil)
147
+ unless call_original.nil?
148
+ puts "test_consume_message(call_original: true) is deprecated and will be removed in the future. You can remove the call_original parameter."
235
149
  end
236
- Phobos::Actions::ProcessMessage.new(
237
- listener: listener,
238
- message: message,
239
- listener_metadata: { topic: 'my-topic' }
240
- ).send(:process_message, payload)
241
- end
242
-
243
- # Check to see that a given message will fail due to validation errors.
244
- # @param handler_class [Class]
245
- # @param payload [Hash]
246
- # @return [void]
247
- def test_consume_invalid_message(handler_class, payload)
248
- expect {
249
- handler_class.decoder.validate(payload,
250
- schema: handler_class.decoder.schema)
251
- }.to raise_error(Avro::SchemaValidator::ValidationError)
150
+ test_consume_batch(handler_class_or_topic, [payload], keys: [key], partition_keys: [partition_key], single: true)
252
151
  end
253
152
 
254
153
  # Test that a given handler will consume a given batch payload correctly,
@@ -258,165 +157,41 @@ module Deimos
258
157
  # @param handler_class_or_topic [Class, String] Class which inherits from
259
158
  # Deimos::Consumer or the topic as a string
260
159
  # @param payloads [Array<Hash>] the payload to consume
261
- # @param keys [Array<Hash,String>]
262
- # @param partition_keys [Array<Integer>]
263
- # @param call_original [Boolean]
264
- # @param skip_expectation [Boolean]
160
+ # @param call_original [Boolean,nil] legacy parameter.
161
+ # @param keys [Array<Object>]
162
+ # @param partition_keys [Array<Object>]
163
+ # @param single [Boolean] used internally.
265
164
  # @return [void]
266
165
  def test_consume_batch(handler_class_or_topic,
267
166
  payloads,
268
167
  keys: [],
269
- partition_keys: [],
270
- call_original: false,
271
- skip_expectation: false,
272
- &block)
273
- if call_original && block_given?
274
- raise 'Cannot have both call_original and be given a block!'
275
- end
276
-
277
- topic_name = 'my-topic'
278
- handler_class = if handler_class_or_topic.is_a?(String)
279
- _get_handler_class_from_topic(handler_class_or_topic)
280
- else
281
- handler_class_or_topic
282
- end
283
- handler = handler_class.new
284
- allow(handler_class).to receive(:new).and_return(handler)
285
- listener = double('listener',
286
- handler_class: handler_class,
287
- encoding: nil)
288
- batch_messages = payloads.zip(keys, partition_keys).map do |payload, key, partition_key|
289
- key ||= _key_from_consumer(handler_class)
290
-
291
- double('message',
292
- 'key' => key,
293
- 'partition_key' => partition_key,
294
- 'partition' => 1,
295
- 'offset' => 1,
296
- 'headers' => {},
297
- 'value' => payload)
298
- end
299
- batch = double('fetched_batch',
300
- 'messages' => batch_messages,
301
- 'topic' => topic_name,
302
- 'partition' => 1,
303
- 'offset_lag' => 0)
304
- unless skip_expectation
305
- _handler_expectation(:consume_batch,
306
- payloads,
307
- handler,
308
- call_original,
309
- &block)
310
- end
311
- action = Phobos::Actions::ProcessBatchInline.new(
312
- listener: listener,
313
- batch: batch,
314
- metadata: { topic: topic_name }
315
- )
316
- allow(action).to receive(:backoff_interval).and_return(0)
317
- allow(action).to receive(:handle_error) { |e| raise e }
318
- action.send(:execute)
319
- end
320
-
321
- # Check to see that a given message will fail due to validation errors.
322
- # @param handler_class [Class]
323
- # @param payloads [Array<Hash>]
324
- # @return [void]
325
- def test_consume_batch_invalid_message(handler_class, payloads)
326
- topic_name = 'my-topic'
327
- handler = handler_class.new
328
- allow(handler_class).to receive(:new).and_return(handler)
329
- listener = double('listener',
330
- handler_class: handler_class,
331
- encoding: nil)
332
- batch_messages = payloads.map do |payload|
333
- key ||= _key_from_consumer(handler_class)
334
-
335
- double('message',
336
- 'key' => key,
337
- 'partition' => 1,
338
- 'offset' => 1,
339
- 'value' => payload)
340
- end
341
- batch = double('fetched_batch',
342
- 'messages' => batch_messages,
343
- 'topic' => topic_name,
344
- 'partition' => 1,
345
- 'offset_lag' => 0)
346
-
347
- action = Phobos::Actions::ProcessBatchInline.new(
348
- listener: listener,
349
- batch: batch,
350
- metadata: { topic: topic_name }
351
- )
352
- allow(action).to receive(:backoff_interval).and_return(0)
353
- allow(action).to receive(:handle_error) { |e| raise e }
354
-
355
- expect { action.send(:execute) }.
356
- to raise_error
357
- end
358
-
359
- private
360
-
361
- def _key_from_consumer(consumer)
362
- if consumer.config[:key_field]
363
- { consumer.config[:key_field] => 1 }
364
- elsif consumer.config[:key_schema]
365
- backend = consumer.decoder
366
- old_schema = backend.schema
367
- backend.schema = consumer.config[:key_schema]
368
- key = backend.schema_fields.map { |field| [field.name, 1] }.to_h
369
- backend.schema = old_schema
370
- key
371
- elsif consumer.config[:no_keys]
372
- nil
168
+ call_original: nil,
169
+ single: false,
170
+ partition_keys: [])
171
+ unless call_original.nil?
172
+ puts "test_consume_batch(call_original: true) is deprecated and will be removed in the future. You can remove the call_original parameter."
173
+ end
174
+ consumer = nil
175
+ topic_name = nil
176
+ if handler_class_or_topic.is_a?(String)
177
+ topic_name = handler_class_or_topic
178
+ consumer = karafka.consumer_for(topic_name)
373
179
  else
374
- 1
180
+ topic_name = Deimos.topic_for_consumer(handler_class_or_topic)
181
+ consumer = karafka.consumer_for(topic_name)
375
182
  end
376
- end
377
-
378
- # @param topic [String]
379
- # @return [Class]
380
- def _get_handler_class_from_topic(topic)
381
- listeners = Phobos.config['listeners']
382
- handler = listeners.find { |l| l.topic == topic }
383
- raise "No consumer found in Phobos configuration for topic #{topic}!" if handler.nil?
384
-
385
- handler.handler.constantize
386
- end
387
-
388
- # Test that a given handler will execute a `method` on an `input` correctly,
389
- # If a block is given, that block will be executed when `method` is called.
390
- # Otherwise it will just confirm that `method` is called at all.
391
- # @param method [Symbol]
392
- # @param input [Object]
393
- # @param handler [Deimos::Consumer]
394
- # @param call_original [Boolean]
395
- def _handler_expectation(method,
396
- input,
397
- handler,
398
- call_original,
399
- &block)
400
- schema_class = handler.class.config[:schema]
401
- namespace = handler.class.config[:namespace]
402
- expected = input.dup
403
183
 
404
- config = handler.class.config
405
- use_schema_classes = config[:use_schema_classes]
406
- use_schema_classes = use_schema_classes.present? ? use_schema_classes : Deimos.config.schema.use_schema_classes
184
+ Deimos.karafka_config_for(topic: topic_name).each_message(single)
407
185
 
408
- if use_schema_classes && schema_class.present?
409
- expected = if input.is_a?(Array)
410
- input.map do |payload|
411
- Utils::SchemaClass.instance(payload, schema_class, namespace)
412
- end
413
- else
414
- Utils::SchemaClass.instance(input, schema_class, namespace)
415
- end
186
+ payloads.each_with_index do |payload, i|
187
+ karafka.produce(payload, {key: keys[i], partition_key: partition_keys[i], topic: consumer.topic.name})
188
+ end
189
+ if block_given?
190
+ allow_any_instance_of(consumer_class).to receive(:consume_batch) do
191
+ yield
192
+ end
193
+ end
194
+ consumer.consume
416
195
  end
417
-
418
- expectation = expect(handler).to receive(method).with(expected, anything, &block)
419
- expectation.and_call_original if call_original
420
- end
421
196
  end
422
197
  end
@@ -9,14 +9,14 @@ module Deimos
9
9
  # @param options [Hash] Options for the span
10
10
  # @return [Object] The span object
11
11
  def start(span_name, options={})
12
- raise NotImplementedError
12
+ raise MissingImplementationError
13
13
  end
14
14
 
15
15
  # Finishes the trace on the span object.
16
16
  # @param span [Object] The span to finish trace on
17
17
  # @return [void]
18
18
  def finish(span)
19
- raise NotImplementedError
19
+ raise MissingImplementationError
20
20
  end
21
21
 
22
22
  # Set an error on the span.
@@ -24,13 +24,13 @@ module Deimos
24
24
  # @param exception [Exception] The exception that occurred
25
25
  # @return [void]
26
26
  def set_error(span, exception)
27
- raise NotImplementedError
27
+ raise MissingImplementationError
28
28
  end
29
29
 
30
30
  # Get the currently activated span.
31
31
  # @return [Object]
32
32
  def active_span
33
- raise NotImplementedError
33
+ raise MissingImplementationError
34
34
  end
35
35
 
36
36
  # Set a tag to a span. Use the currently active span if not given.
@@ -39,13 +39,13 @@ module Deimos
39
39
  # @param span [Object]
40
40
  # @return [void]
41
41
  def set_tag(tag, value, span=nil)
42
- raise NotImplementedError
42
+ raise MissingImplementationError
43
43
  end
44
44
 
45
45
  # Get a tag from a span with the specified tag.
46
46
  # @param tag [String]
47
47
  def get_tag(tag)
48
- raise NotImplementedError
48
+ raise MissingImplementationError
49
49
  end
50
50
 
51
51
  end
@@ -0,0 +1,88 @@
1
+ module Deimos
2
+ class Transcoder
3
+
4
+ attr_accessor :key_field, :backend
5
+
6
+ # @param schema [String]
7
+ # @param namespace [String]
8
+ # @param key_field [Symbol]
9
+ # @param use_schema_classes [Boolean]
10
+ # @param topic [String]
11
+ def initialize(schema:, namespace:, key_field: nil, use_schema_classes: nil, topic: nil)
12
+ @schema = schema
13
+ @namespace = namespace
14
+ self.key_field = key_field
15
+ @use_schema_classes = use_schema_classes
16
+ @topic = topic
17
+ end
18
+
19
+ # @return [Class < Deimos::SchemaBackends::Base]
20
+ def backend
21
+ @backend ||= Deimos.schema_backend(schema: @schema, namespace: @namespace)
22
+ end
23
+
24
+ # for use in test helpers
25
+ # @param key [Object]
26
+ # @return [String]
27
+ def encode_key(key)
28
+ if self.key_field
29
+ self.backend.encode_key(self.key_field, key, topic: @topic)
30
+ else
31
+ self.backend.encode(key, topic: @topic)
32
+ end
33
+ end
34
+
35
+ # @param key [String]
36
+ # @return [Object]
37
+ def decode_key(key)
38
+ return nil if key.nil? || self.key_field.nil?
39
+
40
+ decoded_key = self.backend.decode_key(key, self.key_field)
41
+ return decoded_key unless @use_schema_classes
42
+
43
+ Utils::SchemaClass.instance(decoded_key,
44
+ "#{@schema}_key",
45
+ @namespace)
46
+ end
47
+
48
+ # @param payload [String]
49
+ # @return [Object]
50
+ def decode_message(payload)
51
+ return nil if payload.nil?
52
+
53
+ decoded_payload = self.backend.decode(payload)
54
+ return decoded_payload unless @use_schema_classes
55
+
56
+ Utils::SchemaClass.instance(decoded_payload,
57
+ @schema,
58
+ @namespace)
59
+ end
60
+
61
+ # @param payload [Object]
62
+ # @return [String]
63
+ def encode(payload)
64
+ return nil if payload.nil?
65
+
66
+ self.backend.encode(payload)
67
+ end
68
+
69
+ # @param message [Karafka::Messages::Message]
70
+ # @return [Object]
71
+ def call(message)
72
+ if self.key_field
73
+ decode_key(message.raw_key)
74
+ elsif message.respond_to?(:raw_payload)
75
+ decode_message(message.raw_payload)
76
+ else
77
+ decode_message(message.raw_key)
78
+ end
79
+ end
80
+
81
+ # @param payload [String]
82
+ # @return [Object]
83
+ def decode_message_hash(payload)
84
+ self.key_field ? decode_key(payload) : decode_message(payload)
85
+ end
86
+
87
+ end
88
+ end