deimos-ruby 1.6.1 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +46 -46
- data/deimos-ruby.gemspec +1 -1
- data/lib/deimos/active_record_consumer.rb +1 -1
- data/lib/deimos/consumer.rb +1 -1
- data/lib/deimos/kafka_source.rb +29 -23
- data/lib/deimos/version.rb +1 -1
- data/spec/active_record_consumer_spec.rb +19 -0
- data/spec/kafka_source_spec.rb +53 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53aab4ddcc19808cf7149ed226de9b7ddda07d12ed4237c157cce75780379c6c
|
4
|
+
data.tar.gz: 74ebf879894b88fc4ce5082d4f8b82f8f79a0ef581bf368f25423a755d71d635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c0fa67f0b958ddc3083c278c5c195f896b134f822168df245e906e89f5f5414f063175e2ed1a444891a210d28e7a0d96836806b6ba2efd6f55bf46d929450c1
|
7
|
+
data.tar.gz: 9fc31899551ec75dc0ca1e7ac02cc3f4ed429ec8eb25f42057024d2dc9cde5bef70eabafcf8276e7a8a4ae2b7a88b4efff5efe047715612bd5f5480ce1f52576
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
## 1.6.2 - 2020-05-04
|
11
|
+
### Fixes :wrench:
|
12
|
+
- When saving records via `ActiveRecordConsumer`, update `updated_at` to today's time
|
13
|
+
even if nothing else was saved.
|
14
|
+
- When logging payloads and metadata, decode them first.
|
15
|
+
- Fixes bug in `KafkaSource` that crashes when importing a mix of existing and new records with the `:on_duplicate_key_update` option.
|
16
|
+
|
10
17
|
## [1.6.1] - 2020-04-20
|
11
18
|
### Fixes :wrench:
|
12
19
|
- Re-consuming a message after crashing would try to re-decode message keys.
|
data/Gemfile.lock
CHANGED
@@ -9,45 +9,45 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actioncable (5.2.4)
|
13
|
-
actionpack (= 5.2.4)
|
12
|
+
actioncable (5.2.4.2)
|
13
|
+
actionpack (= 5.2.4.2)
|
14
14
|
nio4r (~> 2.0)
|
15
15
|
websocket-driver (>= 0.6.1)
|
16
|
-
actionmailer (5.2.4)
|
17
|
-
actionpack (= 5.2.4)
|
18
|
-
actionview (= 5.2.4)
|
19
|
-
activejob (= 5.2.4)
|
16
|
+
actionmailer (5.2.4.2)
|
17
|
+
actionpack (= 5.2.4.2)
|
18
|
+
actionview (= 5.2.4.2)
|
19
|
+
activejob (= 5.2.4.2)
|
20
20
|
mail (~> 2.5, >= 2.5.4)
|
21
21
|
rails-dom-testing (~> 2.0)
|
22
|
-
actionpack (5.2.4)
|
23
|
-
actionview (= 5.2.4)
|
24
|
-
activesupport (= 5.2.4)
|
25
|
-
rack (~> 2.0)
|
22
|
+
actionpack (5.2.4.2)
|
23
|
+
actionview (= 5.2.4.2)
|
24
|
+
activesupport (= 5.2.4.2)
|
25
|
+
rack (~> 2.0, >= 2.0.8)
|
26
26
|
rack-test (>= 0.6.3)
|
27
27
|
rails-dom-testing (~> 2.0)
|
28
28
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
29
|
-
actionview (5.2.4)
|
30
|
-
activesupport (= 5.2.4)
|
29
|
+
actionview (5.2.4.2)
|
30
|
+
activesupport (= 5.2.4.2)
|
31
31
|
builder (~> 3.1)
|
32
32
|
erubi (~> 1.4)
|
33
33
|
rails-dom-testing (~> 2.0)
|
34
34
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
35
|
-
activejob (5.2.4)
|
36
|
-
activesupport (= 5.2.4)
|
35
|
+
activejob (5.2.4.2)
|
36
|
+
activesupport (= 5.2.4.2)
|
37
37
|
globalid (>= 0.3.6)
|
38
|
-
activemodel (5.2.4)
|
39
|
-
activesupport (= 5.2.4)
|
40
|
-
activerecord (5.2.4)
|
41
|
-
activemodel (= 5.2.4)
|
42
|
-
activesupport (= 5.2.4)
|
38
|
+
activemodel (5.2.4.2)
|
39
|
+
activesupport (= 5.2.4.2)
|
40
|
+
activerecord (5.2.4.2)
|
41
|
+
activemodel (= 5.2.4.2)
|
42
|
+
activesupport (= 5.2.4.2)
|
43
43
|
arel (>= 9.0)
|
44
44
|
activerecord-import (1.0.3)
|
45
45
|
activerecord (>= 3.2)
|
46
|
-
activestorage (5.2.4)
|
47
|
-
actionpack (= 5.2.4)
|
48
|
-
activerecord (= 5.2.4)
|
46
|
+
activestorage (5.2.4.2)
|
47
|
+
actionpack (= 5.2.4.2)
|
48
|
+
activerecord (= 5.2.4.2)
|
49
49
|
marcel (~> 0.3.1)
|
50
|
-
activesupport (5.2.4)
|
50
|
+
activesupport (5.2.4.2)
|
51
51
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
52
52
|
i18n (>= 0.7, < 2)
|
53
53
|
minitest (~> 5.1)
|
@@ -64,7 +64,7 @@ GEM
|
|
64
64
|
concurrent-ruby (1.1.5)
|
65
65
|
concurrent-ruby-ext (1.1.5)
|
66
66
|
concurrent-ruby (= 1.1.5)
|
67
|
-
crass (1.0.
|
67
|
+
crass (1.0.6)
|
68
68
|
ddtrace (0.30.0)
|
69
69
|
msgpack
|
70
70
|
diff-lcs (1.3)
|
@@ -94,7 +94,7 @@ GEM
|
|
94
94
|
guard-rubocop (1.3.0)
|
95
95
|
guard (~> 2.0)
|
96
96
|
rubocop (~> 0.20)
|
97
|
-
i18n (1.
|
97
|
+
i18n (1.8.2)
|
98
98
|
concurrent-ruby (~> 1.0)
|
99
99
|
jaro_winkler (1.5.4)
|
100
100
|
listen (3.2.1)
|
@@ -104,7 +104,7 @@ GEM
|
|
104
104
|
logging (2.2.2)
|
105
105
|
little-plugger (~> 1.1)
|
106
106
|
multi_json (~> 1.10)
|
107
|
-
loofah (2.
|
107
|
+
loofah (2.5.0)
|
108
108
|
crass (~> 1.0.2)
|
109
109
|
nokogiri (>= 1.5.9)
|
110
110
|
lumberjack (1.0.13)
|
@@ -113,16 +113,16 @@ GEM
|
|
113
113
|
marcel (0.3.3)
|
114
114
|
mimemagic (~> 0.3.2)
|
115
115
|
method_source (0.9.2)
|
116
|
-
mimemagic (0.3.
|
116
|
+
mimemagic (0.3.4)
|
117
117
|
mini_mime (1.0.2)
|
118
118
|
mini_portile2 (2.4.0)
|
119
|
-
minitest (5.
|
119
|
+
minitest (5.14.0)
|
120
120
|
msgpack (1.3.1)
|
121
121
|
multi_json (1.14.1)
|
122
122
|
mysql2 (0.5.3)
|
123
123
|
nenv (0.3.0)
|
124
124
|
nio4r (2.5.2)
|
125
|
-
nokogiri (1.10.
|
125
|
+
nokogiri (1.10.9)
|
126
126
|
mini_portile2 (~> 2.4.0)
|
127
127
|
notiffany (0.1.3)
|
128
128
|
nenv (~> 0.1)
|
@@ -142,30 +142,30 @@ GEM
|
|
142
142
|
pry (0.12.2)
|
143
143
|
coderay (~> 1.1.0)
|
144
144
|
method_source (~> 0.9.0)
|
145
|
-
rack (2.
|
145
|
+
rack (2.2.2)
|
146
146
|
rack-test (1.1.0)
|
147
147
|
rack (>= 1.0, < 3)
|
148
|
-
rails (5.2.4)
|
149
|
-
actioncable (= 5.2.4)
|
150
|
-
actionmailer (= 5.2.4)
|
151
|
-
actionpack (= 5.2.4)
|
152
|
-
actionview (= 5.2.4)
|
153
|
-
activejob (= 5.2.4)
|
154
|
-
activemodel (= 5.2.4)
|
155
|
-
activerecord (= 5.2.4)
|
156
|
-
activestorage (= 5.2.4)
|
157
|
-
activesupport (= 5.2.4)
|
148
|
+
rails (5.2.4.2)
|
149
|
+
actioncable (= 5.2.4.2)
|
150
|
+
actionmailer (= 5.2.4.2)
|
151
|
+
actionpack (= 5.2.4.2)
|
152
|
+
actionview (= 5.2.4.2)
|
153
|
+
activejob (= 5.2.4.2)
|
154
|
+
activemodel (= 5.2.4.2)
|
155
|
+
activerecord (= 5.2.4.2)
|
156
|
+
activestorage (= 5.2.4.2)
|
157
|
+
activesupport (= 5.2.4.2)
|
158
158
|
bundler (>= 1.3.0)
|
159
|
-
railties (= 5.2.4)
|
159
|
+
railties (= 5.2.4.2)
|
160
160
|
sprockets-rails (>= 2.0.0)
|
161
161
|
rails-dom-testing (2.0.3)
|
162
162
|
activesupport (>= 4.2.0)
|
163
163
|
nokogiri (>= 1.6)
|
164
164
|
rails-html-sanitizer (1.3.0)
|
165
165
|
loofah (~> 2.3)
|
166
|
-
railties (5.2.4)
|
167
|
-
actionpack (= 5.2.4)
|
168
|
-
activesupport (= 5.2.4)
|
166
|
+
railties (5.2.4.2)
|
167
|
+
actionpack (= 5.2.4.2)
|
168
|
+
activesupport (= 5.2.4.2)
|
169
169
|
method_source
|
170
170
|
rake (>= 0.8.7)
|
171
171
|
thor (>= 0.19.0, < 2.0)
|
@@ -212,7 +212,7 @@ GEM
|
|
212
212
|
sqlite3 (1.4.1)
|
213
213
|
thor (1.0.1)
|
214
214
|
thread_safe (0.3.6)
|
215
|
-
tzinfo (1.2.
|
215
|
+
tzinfo (1.2.7)
|
216
216
|
thread_safe (~> 0.1)
|
217
217
|
unicode-display_width (1.6.0)
|
218
218
|
websocket-driver (0.7.1)
|
@@ -235,7 +235,7 @@ DEPENDENCIES
|
|
235
235
|
guard-rubocop (~> 1)
|
236
236
|
mysql2 (~> 0.5)
|
237
237
|
pg (~> 1.1)
|
238
|
-
rails (~> 5.2)
|
238
|
+
rails (~> 5.2, >= 5.2.4.2)
|
239
239
|
rake (~> 13)
|
240
240
|
rspec (~> 3)
|
241
241
|
rspec_junit_formatter (~> 0.3)
|
data/deimos-ruby.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency('guard-rubocop', '~> 1')
|
34
34
|
spec.add_development_dependency('mysql2', '~> 0.5')
|
35
35
|
spec.add_development_dependency('pg', '~> 1.1')
|
36
|
-
spec.add_development_dependency('rails', '~> 5.2')
|
36
|
+
spec.add_development_dependency('rails', '~> 5.2', '>= 5.2.4.2')
|
37
37
|
spec.add_development_dependency('rake', '~> 13')
|
38
38
|
spec.add_development_dependency('rspec', '~> 3')
|
39
39
|
spec.add_development_dependency('rspec_junit_formatter', '~>0.3')
|
@@ -51,7 +51,7 @@ module Deimos
|
|
51
51
|
record.send("#{k}=", v)
|
52
52
|
end
|
53
53
|
record.created_at ||= Time.zone.now if record.respond_to?(:created_at)
|
54
|
-
record.updated_at
|
54
|
+
record.updated_at = Time.zone.now if record.respond_to?(:updated_at)
|
55
55
|
record.save!
|
56
56
|
end
|
57
57
|
|
data/lib/deimos/consumer.rb
CHANGED
@@ -15,12 +15,12 @@ module Deimos
|
|
15
15
|
|
16
16
|
# :nodoc:
|
17
17
|
def around_consume(payload, metadata)
|
18
|
-
_received_message(payload, metadata)
|
19
18
|
benchmark = Benchmark.measure do
|
20
19
|
_with_error_span(payload, metadata) do
|
21
20
|
new_metadata = metadata.dup
|
22
21
|
new_metadata[:key] = decode_key(metadata[:key]) if self.class.config[:key_configured]
|
23
22
|
decoded_payload = payload ? self.class.decoder.decode(payload) : nil
|
23
|
+
_received_message(payload, metadata)
|
24
24
|
yield decoded_payload, new_metadata
|
25
25
|
end
|
26
26
|
end
|
data/lib/deimos/kafka_source.rb
CHANGED
@@ -93,31 +93,37 @@ module Deimos
|
|
93
93
|
|
94
94
|
# This will contain an array of hashes, where each hash is the actual
|
95
95
|
# attribute hash that created the object.
|
96
|
-
ids = if results.is_a?(Array)
|
97
|
-
results[1]
|
98
|
-
elsif results.respond_to?(:ids)
|
99
|
-
results.ids
|
100
|
-
else
|
101
|
-
[]
|
102
|
-
end
|
103
|
-
if ids.blank?
|
104
|
-
# re-fill IDs based on what was just entered into the DB.
|
105
|
-
if self.connection.adapter_name.downcase =~ /sqlite/
|
106
|
-
last_id = self.connection.select_value('select last_insert_rowid()')
|
107
|
-
ids = ((last_id - array_of_attributes.size + 1)..last_id).to_a
|
108
|
-
else # mysql
|
109
|
-
last_id = self.connection.select_value('select LAST_INSERT_ID()')
|
110
|
-
ids = (last_id..(last_id + array_of_attributes.size)).to_a
|
111
|
-
end
|
112
|
-
end
|
113
96
|
array_of_hashes = []
|
114
|
-
array_of_attributes.
|
115
|
-
|
116
|
-
|
117
|
-
|
97
|
+
array_of_attributes.each do |array|
|
98
|
+
array_of_hashes << column_names.zip(array).to_h.with_indifferent_access
|
99
|
+
end
|
100
|
+
hashes_with_id, hashes_without_id = array_of_hashes.partition { |arr| arr[:id].present? }
|
101
|
+
|
102
|
+
self.kafka_producers.each { |p| p.send_events(hashes_with_id) }
|
103
|
+
|
104
|
+
if hashes_without_id.any?
|
105
|
+
if options[:on_duplicate_key_update].present? &&
|
106
|
+
options[:on_duplicate_key_update] != [:updated_at]
|
107
|
+
unique_columns = column_names.map(&:to_s) -
|
108
|
+
options[:on_duplicate_key_update].map(&:to_s) - %w(id created_at)
|
109
|
+
records = hashes_without_id.map do |hash|
|
110
|
+
self.where(unique_columns.map { |c| [c, hash[c]] }.to_h).first
|
111
|
+
end
|
112
|
+
self.kafka_producers.each { |p| p.send_events(records) }
|
113
|
+
else
|
114
|
+
# re-fill IDs based on what was just entered into the DB.
|
115
|
+
last_id = if self.connection.adapter_name.downcase =~ /sqlite/
|
116
|
+
self.connection.select_value('select last_insert_rowid()') -
|
117
|
+
hashes_without_id.size + 1
|
118
|
+
else # mysql
|
119
|
+
self.connection.select_value('select LAST_INSERT_ID()')
|
120
|
+
end
|
121
|
+
hashes_without_id.each_with_index do |attrs, i|
|
122
|
+
attrs[:id] = last_id + i
|
123
|
+
end
|
124
|
+
self.kafka_producers.each { |p| p.send_events(hashes_without_id) }
|
125
|
+
end
|
118
126
|
end
|
119
|
-
|
120
|
-
self.kafka_producers.each { |p| p.send_events(array_of_hashes) }
|
121
127
|
results
|
122
128
|
end
|
123
129
|
end
|
data/lib/deimos/version.rb
CHANGED
@@ -22,6 +22,10 @@ module ActiveRecordProducerTest
|
|
22
22
|
Widget.reset_column_information
|
23
23
|
end
|
24
24
|
|
25
|
+
before(:each) do
|
26
|
+
Widget.delete_all
|
27
|
+
end
|
28
|
+
|
25
29
|
after(:all) do
|
26
30
|
ActiveRecord::Base.connection.drop_table(:widgets)
|
27
31
|
end
|
@@ -101,6 +105,21 @@ module ActiveRecordProducerTest
|
|
101
105
|
|
102
106
|
end
|
103
107
|
|
108
|
+
it 'should update only updated_at' do
|
109
|
+
travel_to Time.local(2020, 5, 5, 5, 5, 5)
|
110
|
+
widget1 = Widget.create!(test_id: 'id1', some_int: 3)
|
111
|
+
expect(widget1.updated_at.in_time_zone).to eq(Time.local(2020, 5, 5, 5, 5, 5))
|
112
|
+
|
113
|
+
travel 1.day
|
114
|
+
test_consume_message(MyCustomFetchConsumer, {
|
115
|
+
test_id: 'id1',
|
116
|
+
some_int: 3
|
117
|
+
}, { call_original: true })
|
118
|
+
expect(widget1.reload.updated_at.in_time_zone).
|
119
|
+
to eq(Time.local(2020, 5, 6, 5, 5, 5))
|
120
|
+
travel_back
|
121
|
+
end
|
122
|
+
|
104
123
|
it 'should find widgets by custom logic' do
|
105
124
|
widget1 = Widget.create!(test_id: 'id1')
|
106
125
|
expect(widget1.some_int).to be_nil
|
data/spec/kafka_source_spec.rb
CHANGED
@@ -126,6 +126,59 @@ module KafkaSourceSpec
|
|
126
126
|
}, widgets[2].id)
|
127
127
|
end
|
128
128
|
|
129
|
+
it 'should send events on import with on_duplicate_key_update and existing records' do
|
130
|
+
widget1 = Widget.create(widget_id: 1, name: 'Widget 1')
|
131
|
+
widget2 = Widget.create(widget_id: 2, name: 'Widget 2')
|
132
|
+
widget1.name = 'New Widget 1'
|
133
|
+
widget2.name = 'New Widget 2'
|
134
|
+
Widget.import([widget1, widget2], on_duplicate_key_update: %i(widget_id name))
|
135
|
+
|
136
|
+
expect('my-topic').to have_sent({
|
137
|
+
widget_id: 1,
|
138
|
+
name: 'New Widget 1',
|
139
|
+
id: widget1.id,
|
140
|
+
created_at: anything,
|
141
|
+
updated_at: anything
|
142
|
+
}, widget1.id)
|
143
|
+
expect('my-topic').to have_sent({
|
144
|
+
widget_id: 2,
|
145
|
+
name: 'New Widget 2',
|
146
|
+
id: widget2.id,
|
147
|
+
created_at: anything,
|
148
|
+
updated_at: anything
|
149
|
+
}, widget2.id)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should not fail when mixing existing and new records for import :on_duplicate_key_update' do
|
153
|
+
widget1 = Widget.create(widget_id: 1, name: 'Widget 1')
|
154
|
+
expect('my-topic').to have_sent({
|
155
|
+
widget_id: 1,
|
156
|
+
name: 'Widget 1',
|
157
|
+
id: widget1.id,
|
158
|
+
created_at: anything,
|
159
|
+
updated_at: anything
|
160
|
+
}, widget1.id)
|
161
|
+
|
162
|
+
widget2 = Widget.new(widget_id: 2, name: 'Widget 2')
|
163
|
+
widget1.name = 'New Widget 1'
|
164
|
+
Widget.import([widget1, widget2], on_duplicate_key_update: %i(widget_id))
|
165
|
+
widgets = Widget.all
|
166
|
+
expect('my-topic').to have_sent({
|
167
|
+
widget_id: 1,
|
168
|
+
name: 'New Widget 1',
|
169
|
+
id: widgets[0].id,
|
170
|
+
created_at: anything,
|
171
|
+
updated_at: anything
|
172
|
+
}, widgets[0].id)
|
173
|
+
expect('my-topic').to have_sent({
|
174
|
+
widget_id: 2,
|
175
|
+
name: 'Widget 2',
|
176
|
+
id: widgets[1].id,
|
177
|
+
created_at: anything,
|
178
|
+
updated_at: anything
|
179
|
+
}, widgets[1].id)
|
180
|
+
end
|
181
|
+
|
129
182
|
it 'should send events even if the save fails' do
|
130
183
|
widget = Widget.create!(widget_id: 1, name: 'widget')
|
131
184
|
expect('my-topic').to have_sent({
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deimos-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04
|
11
|
+
date: 2020-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro_turf
|
@@ -213,6 +213,9 @@ dependencies:
|
|
213
213
|
- - "~>"
|
214
214
|
- !ruby/object:Gem::Version
|
215
215
|
version: '5.2'
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: 5.2.4.2
|
216
219
|
type: :development
|
217
220
|
prerelease: false
|
218
221
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -220,6 +223,9 @@ dependencies:
|
|
220
223
|
- - "~>"
|
221
224
|
- !ruby/object:Gem::Version
|
222
225
|
version: '5.2'
|
226
|
+
- - ">="
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: 5.2.4.2
|
223
229
|
- !ruby/object:Gem::Dependency
|
224
230
|
name: rake
|
225
231
|
requirement: !ruby/object:Gem::Requirement
|