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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +0 -17
- data/.tool-versions +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +287 -498
- data/deimos-ruby.gemspec +4 -4
- data/docs/CONFIGURATION.md +133 -226
- data/docs/UPGRADING.md +237 -0
- data/lib/deimos/active_record_consume/batch_consumption.rb +29 -28
- data/lib/deimos/active_record_consume/mass_updater.rb +59 -4
- data/lib/deimos/active_record_consume/message_consumption.rb +15 -21
- data/lib/deimos/active_record_consumer.rb +36 -21
- data/lib/deimos/active_record_producer.rb +28 -9
- data/lib/deimos/backends/base.rb +4 -35
- data/lib/deimos/backends/kafka.rb +6 -22
- data/lib/deimos/backends/kafka_async.rb +6 -22
- data/lib/deimos/backends/{db.rb → outbox.rb} +13 -9
- data/lib/deimos/config/configuration.rb +116 -379
- data/lib/deimos/consume/batch_consumption.rb +24 -124
- data/lib/deimos/consume/message_consumption.rb +36 -63
- data/lib/deimos/consumer.rb +16 -75
- data/lib/deimos/ext/consumer_route.rb +35 -0
- data/lib/deimos/ext/producer_middleware.rb +94 -0
- data/lib/deimos/ext/producer_route.rb +22 -0
- data/lib/deimos/ext/redraw.rb +29 -0
- data/lib/deimos/ext/routing_defaults.rb +72 -0
- data/lib/deimos/ext/schema_route.rb +70 -0
- data/lib/deimos/kafka_message.rb +2 -2
- data/lib/deimos/kafka_source.rb +2 -7
- data/lib/deimos/kafka_topic_info.rb +1 -1
- data/lib/deimos/logging.rb +71 -0
- data/lib/deimos/message.rb +2 -11
- data/lib/deimos/metrics/datadog.rb +40 -1
- data/lib/deimos/metrics/provider.rb +4 -4
- data/lib/deimos/producer.rb +39 -116
- data/lib/deimos/railtie.rb +6 -0
- data/lib/deimos/schema_backends/avro_base.rb +21 -21
- data/lib/deimos/schema_backends/avro_schema_registry.rb +1 -2
- data/lib/deimos/schema_backends/avro_validation.rb +2 -2
- data/lib/deimos/schema_backends/base.rb +19 -12
- data/lib/deimos/schema_backends/mock.rb +6 -1
- data/lib/deimos/schema_backends/plain.rb +47 -0
- data/lib/deimos/schema_class/base.rb +2 -2
- data/lib/deimos/schema_class/enum.rb +1 -1
- data/lib/deimos/schema_class/record.rb +2 -2
- data/lib/deimos/test_helpers.rb +95 -320
- data/lib/deimos/tracing/provider.rb +6 -6
- data/lib/deimos/transcoder.rb +88 -0
- data/lib/deimos/utils/db_poller/base.rb +16 -14
- data/lib/deimos/utils/db_poller/state_based.rb +3 -3
- data/lib/deimos/utils/db_poller/time_based.rb +4 -4
- data/lib/deimos/utils/db_poller.rb +1 -1
- data/lib/deimos/utils/deadlock_retry.rb +1 -1
- data/lib/deimos/utils/{db_producer.rb → outbox_producer.rb} +16 -47
- data/lib/deimos/utils/schema_class.rb +0 -7
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +79 -26
- data/lib/generators/deimos/{db_backend_generator.rb → outbox_backend_generator.rb} +4 -4
- data/lib/generators/deimos/schema_class_generator.rb +0 -1
- data/lib/generators/deimos/v2/templates/karafka.rb.tt +149 -0
- data/lib/generators/deimos/v2_generator.rb +193 -0
- data/lib/tasks/deimos.rake +5 -7
- data/spec/active_record_batch_consumer_association_spec.rb +22 -13
- data/spec/active_record_batch_consumer_spec.rb +84 -65
- data/spec/active_record_consume/batch_consumption_spec.rb +10 -10
- data/spec/active_record_consume/batch_slicer_spec.rb +12 -12
- data/spec/active_record_consume/mass_updater_spec.rb +137 -0
- data/spec/active_record_consumer_spec.rb +29 -13
- data/spec/active_record_producer_spec.rb +36 -26
- data/spec/backends/base_spec.rb +0 -23
- data/spec/backends/kafka_async_spec.rb +1 -3
- data/spec/backends/kafka_spec.rb +1 -3
- data/spec/backends/{db_spec.rb → outbox_spec.rb} +14 -20
- data/spec/batch_consumer_spec.rb +66 -116
- data/spec/consumer_spec.rb +53 -147
- data/spec/deimos_spec.rb +10 -126
- data/spec/kafka_source_spec.rb +19 -52
- data/spec/karafka/karafka.rb +69 -0
- data/spec/karafka_config/karafka_spec.rb +97 -0
- data/spec/logging_spec.rb +25 -0
- data/spec/message_spec.rb +9 -9
- data/spec/producer_spec.rb +112 -254
- data/spec/rake_spec.rb +1 -3
- data/spec/schema_backends/avro_validation_spec.rb +1 -1
- data/spec/schemas/com/my-namespace/MySchemaWithTitle.avsc +22 -0
- data/spec/snapshots/consumers-no-nest.snap +49 -0
- data/spec/snapshots/consumers.snap +49 -0
- data/spec/snapshots/consumers_and_producers-no-nest.snap +49 -0
- data/spec/snapshots/consumers_and_producers.snap +49 -0
- data/spec/snapshots/consumers_circular-no-nest.snap +49 -0
- data/spec/snapshots/consumers_circular.snap +49 -0
- data/spec/snapshots/consumers_complex_types-no-nest.snap +49 -0
- data/spec/snapshots/consumers_complex_types.snap +49 -0
- data/spec/snapshots/consumers_nested-no-nest.snap +49 -0
- data/spec/snapshots/consumers_nested.snap +49 -0
- data/spec/snapshots/namespace_folders.snap +49 -0
- data/spec/snapshots/namespace_map.snap +49 -0
- data/spec/snapshots/producers_with_key-no-nest.snap +49 -0
- data/spec/snapshots/producers_with_key.snap +49 -0
- data/spec/spec_helper.rb +61 -29
- data/spec/utils/db_poller_spec.rb +49 -39
- data/spec/utils/{db_producer_spec.rb → outbox_producer_spec.rb} +17 -184
- metadata +58 -67
- data/lib/deimos/batch_consumer.rb +0 -7
- data/lib/deimos/config/phobos_config.rb +0 -163
- data/lib/deimos/instrumentation.rb +0 -95
- data/lib/deimos/monkey_patches/phobos_cli.rb +0 -35
- data/lib/deimos/utils/inline_consumer.rb +0 -158
- data/lib/deimos/utils/lag_reporter.rb +0 -186
- data/lib/deimos/utils/schema_controller_mixin.rb +0 -129
- data/spec/config/configuration_spec.rb +0 -321
- data/spec/kafka_listener_spec.rb +0 -55
- data/spec/phobos.bad_db.yml +0 -73
- data/spec/phobos.yml +0 -77
- data/spec/utils/inline_consumer_spec.rb +0 -31
- data/spec/utils/lag_reporter_spec.rb +0 -76
- data/spec/utils/platform_schema_validation_spec.rb +0 -0
- data/spec/utils/schema_controller_mixin_spec.rb +0 -84
- /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/migration +0 -0
- /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/rails3_migration +0 -0
data/lib/deimos/test_helpers.rb
CHANGED
@@ -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
|
-
|
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.
|
26
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
103
|
-
delete_if { |k, v|
|
104
|
-
merge!(
|
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::
|
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
|
118
|
-
min_hash_diff = messages.min_by { |m| _hash_diff(m, message)
|
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
|
-
|
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 =
|
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::
|
145
|
-
|
146
|
-
|
147
|
-
|
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? ?
|
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
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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
|
-
|
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
|
262
|
-
# @param
|
263
|
-
# @param
|
264
|
-
# @param
|
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
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|