cyclone_lariat 0.4.0 → 1.0.0.rc1
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 +5 -5
- data/.github/workflows/gem-push.yml +4 -4
- data/.rubocop.yml +9 -5
- data/Gemfile.lock +123 -21
- data/Guardfile +42 -0
- data/README.md +417 -220
- data/bin/cyclone_lariat +75 -43
- data/cyclone_lariat.gemspec +10 -3
- data/lib/cyclone_lariat/clients/abstract.rb +40 -0
- data/lib/cyclone_lariat/clients/sns.rb +163 -0
- data/lib/cyclone_lariat/clients/sqs.rb +114 -0
- data/lib/cyclone_lariat/core.rb +21 -0
- data/lib/cyclone_lariat/errors.rb +16 -0
- data/lib/cyclone_lariat/fake.rb +19 -0
- data/lib/cyclone_lariat/generators/command.rb +53 -0
- data/lib/cyclone_lariat/generators/event.rb +52 -0
- data/lib/cyclone_lariat/generators/queue.rb +30 -0
- data/lib/cyclone_lariat/generators/topic.rb +29 -0
- data/lib/cyclone_lariat/messages/v1/abstract.rb +139 -0
- data/lib/cyclone_lariat/messages/v1/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/validator.rb +31 -0
- data/lib/cyclone_lariat/messages/v2/abstract.rb +149 -0
- data/lib/cyclone_lariat/messages/v2/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/validator.rb +39 -0
- data/lib/cyclone_lariat/middleware.rb +9 -6
- data/lib/cyclone_lariat/migration.rb +54 -117
- data/lib/cyclone_lariat/options.rb +52 -0
- data/lib/cyclone_lariat/presenters/graph.rb +54 -0
- data/lib/cyclone_lariat/presenters/queues.rb +41 -0
- data/lib/cyclone_lariat/presenters/subscriptions.rb +34 -0
- data/lib/cyclone_lariat/presenters/topics.rb +40 -0
- data/lib/cyclone_lariat/publisher.rb +25 -0
- data/lib/cyclone_lariat/repo/active_record/messages.rb +92 -0
- data/lib/cyclone_lariat/repo/active_record/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/messages.rb +43 -0
- data/lib/cyclone_lariat/repo/messages_mapper.rb +49 -0
- data/lib/cyclone_lariat/repo/sequel/messages.rb +73 -0
- data/lib/cyclone_lariat/repo/sequel/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/versions.rb +42 -0
- data/lib/cyclone_lariat/resources/queue.rb +167 -0
- data/lib/cyclone_lariat/resources/topic.rb +132 -0
- data/lib/cyclone_lariat/services/migrate.rb +51 -0
- data/lib/cyclone_lariat/services/rollback.rb +51 -0
- data/lib/cyclone_lariat/version.rb +1 -1
- data/lib/cyclone_lariat.rb +4 -11
- data/lib/tasks/console.rake +1 -1
- data/lib/tasks/cyclone_lariat.rake +10 -12
- data/lib/tasks/db.rake +0 -15
- metadata +127 -27
- data/config/db.example.rb +0 -9
- data/config/initializers/sequel.rb +0 -7
- data/db/migrate/01_add_uuid_extensions.rb +0 -15
- data/db/migrate/02_add_events.rb +0 -19
- data/db/migrate/03_add_versions.rb +0 -9
- data/docs/_imgs/graphviz_01.png +0 -0
- data/docs/_imgs/graphviz_02.png +0 -0
- data/docs/_imgs/graphviz_03.png +0 -0
- data/docs/_imgs/lariat.jpg +0 -0
- data/docs/_imgs/logic.png +0 -0
- data/docs/_imgs/sqs_sns_diagram.png +0 -0
- data/lib/cyclone_lariat/abstract/client.rb +0 -112
- data/lib/cyclone_lariat/abstract/message.rb +0 -98
- data/lib/cyclone_lariat/command.rb +0 -13
- data/lib/cyclone_lariat/configure.rb +0 -15
- data/lib/cyclone_lariat/event.rb +0 -13
- data/lib/cyclone_lariat/messages_mapper.rb +0 -46
- data/lib/cyclone_lariat/messages_repo.rb +0 -60
- data/lib/cyclone_lariat/queue.rb +0 -147
- data/lib/cyclone_lariat/sns_client.rb +0 -149
- data/lib/cyclone_lariat/sqs_client.rb +0 -93
- data/lib/cyclone_lariat/topic.rb +0 -113
@@ -2,19 +2,24 @@
|
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'forwardable'
|
5
|
-
|
6
|
-
|
5
|
+
require 'cyclone_lariat/clients/sns'
|
6
|
+
require 'cyclone_lariat/clients/sqs'
|
7
|
+
require 'cyclone_lariat/repo/versions'
|
8
|
+
require 'cyclone_lariat/services/migrate'
|
9
|
+
require 'cyclone_lariat/services/rollback'
|
10
|
+
require 'cyclone_lariat/presenters/topics'
|
11
|
+
require 'cyclone_lariat/presenters/queues'
|
12
|
+
require 'cyclone_lariat/presenters/subscriptions'
|
13
|
+
require 'cyclone_lariat/presenters/graph'
|
7
14
|
require 'luna_park/errors'
|
8
|
-
require 'terminal-table'
|
9
|
-
require 'set'
|
10
15
|
|
11
16
|
module CycloneLariat
|
12
17
|
class Migration
|
13
18
|
extend Forwardable
|
14
19
|
include LunaPark::Extensions::Injector
|
15
20
|
|
16
|
-
dependency(:sns) { CycloneLariat::
|
17
|
-
dependency(:sqs) { CycloneLariat::
|
21
|
+
dependency(:sns) { CycloneLariat::Clients::Sns.new }
|
22
|
+
dependency(:sqs) { CycloneLariat::Clients::Sqs.new }
|
18
23
|
|
19
24
|
DIR = './lariat/migrate'
|
20
25
|
|
@@ -56,7 +61,9 @@ module CycloneLariat
|
|
56
61
|
)
|
57
62
|
end
|
58
63
|
|
59
|
-
def subscribe(topic:, endpoint:)
|
64
|
+
def subscribe(topic:, endpoint:, policy: nil)
|
65
|
+
policy ||= default_policy(endpoint)
|
66
|
+
sqs.add_policy(queue: endpoint, policy: policy) if endpoint.queue?
|
60
67
|
sns.subscribe topic: topic, endpoint: endpoint
|
61
68
|
puts " Subscription was created `#{topic.name} -> #{endpoint.name}`"
|
62
69
|
end
|
@@ -66,6 +73,23 @@ module CycloneLariat
|
|
66
73
|
puts " Subscription was deleted `#{topic.name} -> #{endpoint.name}`"
|
67
74
|
end
|
68
75
|
|
76
|
+
def default_policy(queue)
|
77
|
+
{
|
78
|
+
'Sid' => queue.arn,
|
79
|
+
'Effect' => 'Allow',
|
80
|
+
'Principal' => {
|
81
|
+
'AWS' => '*'
|
82
|
+
},
|
83
|
+
'Action' => 'SQS:*',
|
84
|
+
'Resource' => queue.arn,
|
85
|
+
'Condition' => {
|
86
|
+
'ArnEquals' => {
|
87
|
+
'aws:SourceArn' => fanout_arn_pattern
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
69
93
|
def topics
|
70
94
|
sns.list_all
|
71
95
|
end
|
@@ -82,132 +106,45 @@ module CycloneLariat
|
|
82
106
|
|
83
107
|
def process(resource:, for_topic:, for_queue:)
|
84
108
|
case resource
|
85
|
-
when Topic then for_topic.call(resource)
|
86
|
-
when Queue then for_queue.call(resource)
|
109
|
+
when Resources::Topic then for_topic.call(resource)
|
110
|
+
when Resources::Queue then for_queue.call(resource)
|
87
111
|
else
|
88
112
|
raise ArgumentError, "Unknown resource class #{resource.class}"
|
89
113
|
end
|
90
114
|
end
|
91
115
|
|
116
|
+
def fanout_arn_pattern
|
117
|
+
@fanout_arn_pattern ||= [
|
118
|
+
'arn:aws:sns',
|
119
|
+
CycloneLariat.config.aws_region,
|
120
|
+
CycloneLariat.config.aws_account_id,
|
121
|
+
"#{CycloneLariat.config.instance}-*-fanout-*"
|
122
|
+
].join(':')
|
123
|
+
end
|
124
|
+
|
92
125
|
class << self
|
93
|
-
def migrate(
|
94
|
-
|
95
|
-
|
96
|
-
Dir.glob("#{dir}/*.rb") do |path|
|
97
|
-
filename = File.basename(path, '.rb')
|
98
|
-
version, title = filename.split('_', 2)
|
99
|
-
|
100
|
-
existed_migrations = dataset.all.map { |row| row[:version] }
|
101
|
-
unless existed_migrations.include? version.to_i
|
102
|
-
class_name = title.split('_').collect(&:capitalize).join
|
103
|
-
puts "Up - #{version} #{class_name} #{path}"
|
104
|
-
require_relative Pathname.new(Dir.pwd) + Pathname.new(path)
|
105
|
-
Object.const_get(class_name).new.up
|
106
|
-
dataset.insert(version: version)
|
107
|
-
end
|
108
|
-
end
|
126
|
+
def migrate(repo: CycloneLariat::Repo::Versions.new, dir: DIR, service: Services::Migrate)
|
127
|
+
puts service.new(repo: repo, dir: dir).call
|
109
128
|
end
|
110
129
|
|
111
|
-
def rollback(version = nil,
|
112
|
-
|
113
|
-
version ||= existed_migrations[-1]
|
114
|
-
migrations_to_downgrade = existed_migrations.select { |migration| migration >= version }
|
115
|
-
|
116
|
-
paths = []
|
117
|
-
migrations_to_downgrade.each do |migration|
|
118
|
-
path = Pathname.new(Dir.pwd) + Pathname.new(dir)
|
119
|
-
founded = Dir.glob("#{path}/#{migration}_*.rb")
|
120
|
-
raise "Could not found migration: `#{migration}` in #{path}" if founded.empty?
|
121
|
-
raise "Found lot of migration: `#{migration}` in #{path}" if founded.size > 1
|
122
|
-
|
123
|
-
paths += founded
|
124
|
-
end
|
125
|
-
|
126
|
-
paths.each do |path|
|
127
|
-
filename = File.basename(path, '.rb')
|
128
|
-
version, title = filename.split('_', 2)
|
129
|
-
class_name = title.split('_').collect(&:capitalize).join
|
130
|
-
puts "Down - #{version} #{class_name} #{path}"
|
131
|
-
require_relative Pathname.new(Dir.pwd) + Pathname.new(path)
|
132
|
-
Object.const_get(class_name).new.down
|
133
|
-
dataset.filter(version: version).delete
|
134
|
-
end
|
130
|
+
def rollback(version = nil, repo: CycloneLariat::Repo::Versions.new, dir: DIR, service: Services::Rollback)
|
131
|
+
puts service.new(repo: repo, dir: dir).call(version)
|
135
132
|
end
|
136
133
|
|
137
|
-
def list_topics
|
138
|
-
|
139
|
-
new.topics.each do |topic|
|
140
|
-
rows << [
|
141
|
-
topic.custom? ? 'custom' : 'standard',
|
142
|
-
topic.region,
|
143
|
-
topic.account_id,
|
144
|
-
topic.name,
|
145
|
-
topic.instance,
|
146
|
-
topic.kind,
|
147
|
-
topic.publisher,
|
148
|
-
topic.type,
|
149
|
-
topic.fifo
|
150
|
-
]
|
151
|
-
end
|
152
|
-
|
153
|
-
puts Terminal::Table.new rows: rows, headings: %w[valid region account_id name instance kind publisher type fifo]
|
134
|
+
def list_topics(presenter: Presenters::Topics)
|
135
|
+
puts presenter.call(new.topics)
|
154
136
|
end
|
155
137
|
|
156
|
-
def list_queues
|
157
|
-
|
158
|
-
new.queues.each do |queue|
|
159
|
-
rows << [
|
160
|
-
queue.custom? ? 'custom' : 'standard',
|
161
|
-
queue.region,
|
162
|
-
queue.account_id,
|
163
|
-
queue.name,
|
164
|
-
queue.instance,
|
165
|
-
queue.kind,
|
166
|
-
queue.publisher,
|
167
|
-
queue.type,
|
168
|
-
queue.dest,
|
169
|
-
queue.fifo
|
170
|
-
]
|
171
|
-
end
|
172
|
-
|
173
|
-
puts Terminal::Table.new rows: rows, headings: %w[valid region account_id name instance kind publisher type destination fifo]
|
138
|
+
def list_queues(presenter: Presenters::Queues)
|
139
|
+
puts presenter.call(new.queues)
|
174
140
|
end
|
175
141
|
|
176
|
-
def list_subscriptions
|
177
|
-
|
178
|
-
new.subscriptions.each do |subscription|
|
179
|
-
rows << [
|
180
|
-
subscription[:topic].name,
|
181
|
-
subscription[:endpoint].name,
|
182
|
-
subscription[:arn]
|
183
|
-
]
|
184
|
-
end
|
185
|
-
|
186
|
-
puts Terminal::Table.new rows: rows, headings: %w[topic endpoint subscription_arn]
|
142
|
+
def list_subscriptions(presenter: Presenters::Subscriptions)
|
143
|
+
puts presenter.call(new.subscriptions)
|
187
144
|
end
|
188
145
|
|
189
|
-
def build_graph
|
190
|
-
|
191
|
-
resources_set = Set.new
|
192
|
-
|
193
|
-
subscriptions.each do |subscription|
|
194
|
-
resources_set << subscription[:topic]
|
195
|
-
resources_set << subscription[:endpoint]
|
196
|
-
end
|
197
|
-
|
198
|
-
puts 'digraph G {'
|
199
|
-
puts ' rankdir=LR;'
|
200
|
-
|
201
|
-
resources_set.each do |resource|
|
202
|
-
color = resource.custom? ? ', fillcolor=grey' : ', fillcolor=white'
|
203
|
-
style = resource.topic? ? "[shape=component style=filled#{color}]" : "[shape=record, style=\"rounded,filled\"#{color}]"
|
204
|
-
puts " \"#{resource.name}\" #{style};"
|
205
|
-
end
|
206
|
-
|
207
|
-
subscriptions.each do |subscription|
|
208
|
-
puts " \"#{subscription[:topic].name}\" -> \"#{subscription[:endpoint].name}\";"
|
209
|
-
end
|
210
|
-
puts '}'
|
146
|
+
def build_graph(presenter: Presenters::Graph)
|
147
|
+
puts presenter.call(new.subscriptions)
|
211
148
|
end
|
212
149
|
end
|
213
150
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'luna_park/values/compound'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
class Options < LunaPark::Values::Compound
|
7
|
+
attr_accessor :aws_key, :aws_secret_key, :publisher,
|
8
|
+
:aws_region, :instance, :aws_account_id,
|
9
|
+
:messages_dataset, :version, :versions_dataset,
|
10
|
+
:driver, :fake_publish
|
11
|
+
|
12
|
+
# @param [CycloneLariat::Options, Hash] other
|
13
|
+
# @return [CycloneLariat::Options]
|
14
|
+
def merge!(other)
|
15
|
+
other = self.class.wrap(other)
|
16
|
+
|
17
|
+
self.aws_key ||= other.aws_key
|
18
|
+
self.aws_secret_key ||= other.aws_secret_key
|
19
|
+
self.publisher ||= other.publisher
|
20
|
+
self.aws_region ||= other.aws_region
|
21
|
+
self.instance ||= other.instance
|
22
|
+
self.aws_account_id ||= other.aws_account_id
|
23
|
+
self.messages_dataset ||= other.messages_dataset
|
24
|
+
self.version ||= other.version
|
25
|
+
self.versions_dataset ||= other.versions_dataset
|
26
|
+
self.driver ||= other.driver
|
27
|
+
self.fake_publish ||= other.fake_publish
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def merge(other)
|
33
|
+
dup.merge!(other)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_h
|
37
|
+
{
|
38
|
+
aws_key: aws_key,
|
39
|
+
aws_secret_key: aws_secret_key,
|
40
|
+
publisher: publisher,
|
41
|
+
aws_region: aws_region,
|
42
|
+
instance: instance,
|
43
|
+
aws_account_id: aws_account_id,
|
44
|
+
messages_dataset: messages_dataset,
|
45
|
+
version: version,
|
46
|
+
versions_dataset: versions_dataset,
|
47
|
+
driver: driver,
|
48
|
+
fake_publish: fake_publish
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
module Presenters
|
7
|
+
class Graph
|
8
|
+
HEADS = %w[topic endpoint subscription_arn].freeze
|
9
|
+
def self.call(subscriptions)
|
10
|
+
new.call(subscriptions)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(subscriptions)
|
14
|
+
return '' if subscriptions.empty?
|
15
|
+
|
16
|
+
resources_set = Set.new
|
17
|
+
|
18
|
+
subscriptions.each do |subscription|
|
19
|
+
resources_set << subscription[:topic]
|
20
|
+
resources_set << subscription[:endpoint]
|
21
|
+
end
|
22
|
+
|
23
|
+
[].tap do |output|
|
24
|
+
output << open_graph
|
25
|
+
|
26
|
+
resources_set.each { |resource| output << present_resource(resource) }
|
27
|
+
subscriptions.each { |subscription| output << present_subscription(subscription) }
|
28
|
+
|
29
|
+
output << close_graph
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def present_resource(resource)
|
36
|
+
color = resource.custom? ? ', fillcolor=grey' : ', fillcolor=white'
|
37
|
+
style = resource.topic? ? "[shape=component style=filled#{color}]" : "[shape=record, style=\"rounded,filled\"#{color}]"
|
38
|
+
" \"#{resource.name}\" #{style};"
|
39
|
+
end
|
40
|
+
|
41
|
+
def present_subscription(subscription)
|
42
|
+
" \"#{subscription[:topic].name}\" -> \"#{subscription[:endpoint].name}\";"
|
43
|
+
end
|
44
|
+
|
45
|
+
def open_graph
|
46
|
+
"digraph G {\n rankdir=LR;"
|
47
|
+
end
|
48
|
+
|
49
|
+
def close_graph
|
50
|
+
'}'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
module Presenters
|
7
|
+
class Queues
|
8
|
+
HEADS = %w[valid region account_id name instance kind publisher type destination fifo].freeze
|
9
|
+
|
10
|
+
def self.call(queues)
|
11
|
+
new.call(queues)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(queues)
|
15
|
+
rows = []
|
16
|
+
queues.each do |queue|
|
17
|
+
rows << row(queue)
|
18
|
+
end
|
19
|
+
|
20
|
+
Terminal::Table.new rows: rows, headings: HEADS
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def row(queue)
|
26
|
+
[
|
27
|
+
queue.custom? ? 'custom' : 'standard',
|
28
|
+
queue.region,
|
29
|
+
queue.account_id,
|
30
|
+
queue.name,
|
31
|
+
queue.instance,
|
32
|
+
queue.kind,
|
33
|
+
queue.publisher,
|
34
|
+
queue.type,
|
35
|
+
queue.dest,
|
36
|
+
queue.fifo
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
module Presenters
|
7
|
+
class Subscriptions
|
8
|
+
HEADS = %w[topic endpoint subscription_arn].freeze
|
9
|
+
|
10
|
+
def self.call(subscriptions)
|
11
|
+
new.call(subscriptions)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(subscriptions)
|
15
|
+
rows = []
|
16
|
+
subscriptions.each do |subscription|
|
17
|
+
rows << row(subscription)
|
18
|
+
end
|
19
|
+
|
20
|
+
Terminal::Table.new rows: rows, headings: HEADS
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def row(subscription)
|
26
|
+
[
|
27
|
+
subscription[:topic].name,
|
28
|
+
subscription[:endpoint].name,
|
29
|
+
subscription[:arn]
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
module Presenters
|
7
|
+
class Topics
|
8
|
+
HEADS = %w[valid region account_id name instance kind publisher type fifo].freeze
|
9
|
+
|
10
|
+
def self.call(topics)
|
11
|
+
new.call(topics)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(topics)
|
15
|
+
rows = []
|
16
|
+
topics.each do |topic|
|
17
|
+
rows << row(topic)
|
18
|
+
end
|
19
|
+
|
20
|
+
Terminal::Table.new rows: rows, headings: HEADS
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def row(topic)
|
26
|
+
[
|
27
|
+
topic.custom? ? 'custom' : 'standard',
|
28
|
+
topic.region,
|
29
|
+
topic.account_id,
|
30
|
+
topic.name,
|
31
|
+
topic.instance,
|
32
|
+
topic.kind,
|
33
|
+
topic.publisher,
|
34
|
+
topic.type,
|
35
|
+
topic.fifo
|
36
|
+
]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cyclone_lariat/clients/sns'
|
4
|
+
require 'cyclone_lariat/clients/sqs'
|
5
|
+
|
6
|
+
module CycloneLariat
|
7
|
+
class Publisher
|
8
|
+
include Generators::Event
|
9
|
+
include Generators::Command
|
10
|
+
|
11
|
+
attr_reader :config
|
12
|
+
|
13
|
+
def initialize(**options)
|
14
|
+
@config = CycloneLariat::Options.wrap(options).merge!(CycloneLariat.config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def sqs
|
18
|
+
@sqs ||= Clients::Sqs.new(**config.to_h)
|
19
|
+
end
|
20
|
+
|
21
|
+
def sns
|
22
|
+
@sns ||= Clients::Sns.new(**config.to_h)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cyclone_lariat/messages/v1/event'
|
4
|
+
require 'cyclone_lariat/messages/v1/command'
|
5
|
+
require 'cyclone_lariat/repo/messages_mapper'
|
6
|
+
|
7
|
+
module CycloneLariat
|
8
|
+
module Repo
|
9
|
+
module ActiveRecord
|
10
|
+
class Messages
|
11
|
+
attr_reader :dataset
|
12
|
+
|
13
|
+
def initialize(dataset)
|
14
|
+
@dataset = dataset
|
15
|
+
end
|
16
|
+
|
17
|
+
def enabled?
|
18
|
+
!dataset.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def disabled?
|
22
|
+
dataset.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(msg)
|
26
|
+
dataset.create(MessagesMapper.to_row(msg)).uuid
|
27
|
+
end
|
28
|
+
|
29
|
+
def exists?(uuid:)
|
30
|
+
dataset.exists?(uuid: uuid)
|
31
|
+
end
|
32
|
+
|
33
|
+
def processed!(uuid:, error: nil)
|
34
|
+
data = { processed_at: current_timestamp_from_db }
|
35
|
+
data.merge!(client_error_message: error.message, client_error_details: JSON.generate(error.details)) if error
|
36
|
+
|
37
|
+
message = dataset.where(uuid: uuid).first
|
38
|
+
return false unless message
|
39
|
+
|
40
|
+
message.update(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def find(uuid:)
|
44
|
+
row = dataset.where(uuid: uuid).first
|
45
|
+
return if row.nil?
|
46
|
+
|
47
|
+
build_message_from_ar_row(row)
|
48
|
+
end
|
49
|
+
|
50
|
+
def each_unprocessed
|
51
|
+
dataset.where(processed_at: nil).each do |row|
|
52
|
+
msg = build_message_from_ar_row(row)
|
53
|
+
yield(msg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_with_client_errors
|
58
|
+
dataset
|
59
|
+
.where.not(processed_at: nil)
|
60
|
+
.where.not(client_error_message: nil)
|
61
|
+
.each do |row|
|
62
|
+
msg = build_message_from_ar_row(row)
|
63
|
+
yield(msg)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def build_message_from_ar_row(row)
|
70
|
+
build MessagesMapper.from_row(row.attributes.symbolize_keys)
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_timestamp_from_db
|
74
|
+
time_from_db =
|
75
|
+
::ActiveRecord::Base
|
76
|
+
.connection.execute('select current_timestamp;')
|
77
|
+
.first
|
78
|
+
time = time_from_db.is_a?(Hash) ? time_from_db['current_timestamp'] : time_from_db[0]
|
79
|
+
time.is_a?(Time) ? time : Time.parse(time)
|
80
|
+
end
|
81
|
+
|
82
|
+
def build(raw)
|
83
|
+
case kind = raw.delete(:kind)
|
84
|
+
when 'event' then CycloneLariat::Messages::V1::Event.wrap raw
|
85
|
+
when 'command' then CycloneLariat::Messages::V1::Command.wrap raw
|
86
|
+
else raise ArgumentError, "Unknown kind `#{kind}` of message"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Repo
|
5
|
+
module ActiveRecord
|
6
|
+
class Versions
|
7
|
+
attr_reader :dataset
|
8
|
+
|
9
|
+
def initialize(dataset)
|
10
|
+
@dataset = dataset
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(version)
|
14
|
+
dataset.create(version: version)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove(version)
|
19
|
+
dataset.where(version: version).delete_all.positive?
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
dataset.pluck(:version).map { |version| { version: version } }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'luna_park/extensions/injector'
|
5
|
+
require 'cyclone_lariat/core'
|
6
|
+
require 'cyclone_lariat/repo/sequel/messages'
|
7
|
+
require 'cyclone_lariat/repo/active_record/messages'
|
8
|
+
|
9
|
+
module CycloneLariat
|
10
|
+
module Repo
|
11
|
+
class Messages
|
12
|
+
include LunaPark::Extensions::Injector
|
13
|
+
|
14
|
+
attr_reader :config
|
15
|
+
|
16
|
+
dependency(:sequel_messages_class) { Repo::Sequel::Messages }
|
17
|
+
dependency(:active_record_messages_class) { Repo::ActiveRecord::Messages }
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
def_delegators :driver, :create, :exists?, :processed!, :find, :each_unprocessed, :each_with_client_errors,
|
22
|
+
:enabled?, :disabled?
|
23
|
+
|
24
|
+
def initialize(**options)
|
25
|
+
@config = CycloneLariat::Options.wrap(options).merge!(CycloneLariat.config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def driver
|
29
|
+
@driver ||= select(driver: config.driver)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def select(driver:)
|
35
|
+
case driver
|
36
|
+
when :sequel then sequel_messages_class.new(config.messages_dataset)
|
37
|
+
when :active_record then active_record_messages_class.new(config.messages_dataset)
|
38
|
+
else raise ArgumentError, "Undefined driver `#{driver}`"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Repo
|
5
|
+
class MessagesMapper
|
6
|
+
class << self
|
7
|
+
def from_row(row)
|
8
|
+
return if row.nil?
|
9
|
+
|
10
|
+
row[:data] = hash_from_json_column(row[:data])
|
11
|
+
row[:client_error_details] = hash_from_json_column(row[:client_error_details]) if row[:client_error_details]
|
12
|
+
row
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_row(input)
|
16
|
+
{
|
17
|
+
uuid: input.uuid,
|
18
|
+
kind: input.kind,
|
19
|
+
type: input.type,
|
20
|
+
publisher: input.publisher,
|
21
|
+
data: JSON.generate(input.data),
|
22
|
+
client_error_message: input.client_error&.message,
|
23
|
+
client_error_details: JSON.generate(input.client_error&.details),
|
24
|
+
version: input.version,
|
25
|
+
sent_at: input.sent_at
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def hash_from_json_column(data)
|
32
|
+
return data if data.is_a?(Hash)
|
33
|
+
return JSON.parse(data) if data.is_a?(String)
|
34
|
+
|
35
|
+
if pg_json_extension_enabled?
|
36
|
+
return data.to_h if data.is_a?(::Sequel::Postgres::JSONHash)
|
37
|
+
return JSON.parse(data.to_s) if data.is_a?(::Sequel::Postgres::JSONString)
|
38
|
+
end
|
39
|
+
|
40
|
+
raise ArgumentError, "Unknown type of `#{data}`"
|
41
|
+
end
|
42
|
+
|
43
|
+
def pg_json_extension_enabled?
|
44
|
+
Object.const_defined?('Sequel::Postgres::JSONHash')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|