event_hub 0.1.0 → 1.0.0
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.yml +0 -3
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -29
- data/README.md +130 -10
- data/event_hub.gemspec +1 -6
- data/lib/event_hub/adapters/test/message.rb +35 -19
- data/lib/event_hub/adapters/test.rb +20 -16
- data/lib/event_hub/adapters.rb +0 -5
- data/lib/event_hub/event.rb +37 -20
- data/lib/event_hub/handler.rb +16 -14
- data/lib/event_hub/message.rb +19 -17
- data/lib/event_hub/version.rb +1 -1
- data/lib/event_hub.rb +6 -2
- metadata +5 -50
- data/lib/event_hub/adapters/aws/message.rb +0 -55
- data/lib/event_hub/adapters/aws.rb +0 -78
- data/lib/event_hub/adapters/bunny/message.rb +0 -32
- data/lib/event_hub/adapters/bunny.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f88590e890094b75995c444528f90a10ba26df7573927c6cbfd3a1db609a3c65
|
4
|
+
data.tar.gz: 6b820bf00fe54147b2c4067f3bfb43a50d77b59f9c8e047939188fc761d6a646
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 906008f796563c57fe06c883ce24323aad1072110a11525ff35da682e4912f02ba336c737411c2999ccdd97b1ea11f58859f21fba478e342c1eab600c9c32b21
|
7
|
+
data.tar.gz: 391b241d8c1957f8a4f27d9f12daf060a50fccf34a5630be4b711b62d60fabcc8c1f3323720d30082d811401a7644d35259d227bc1abb00ec9221b073a3c9c84
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,43 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
event_hub (
|
5
|
-
aws-sdk-sns (~> 1.0)
|
6
|
-
aws-sdk-sqs (~> 1.0)
|
7
|
-
bunny (>= 2.13.0)
|
4
|
+
event_hub (1.0.0)
|
8
5
|
|
9
6
|
GEM
|
10
7
|
remote: https://rubygems.org/
|
11
8
|
specs:
|
12
|
-
amq-protocol (2.3.2)
|
13
9
|
ast (2.4.2)
|
14
|
-
aws-eventstream (1.2.0)
|
15
|
-
aws-partitions (1.770.0)
|
16
|
-
aws-sdk-core (3.173.1)
|
17
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
18
|
-
aws-partitions (~> 1, >= 1.651.0)
|
19
|
-
aws-sigv4 (~> 1.5)
|
20
|
-
jmespath (~> 1, >= 1.6.1)
|
21
|
-
aws-sdk-sns (1.60.0)
|
22
|
-
aws-sdk-core (~> 3, >= 3.165.0)
|
23
|
-
aws-sigv4 (~> 1.1)
|
24
|
-
aws-sdk-sqs (1.55.0)
|
25
|
-
aws-sdk-core (~> 3, >= 3.165.0)
|
26
|
-
aws-sigv4 (~> 1.1)
|
27
|
-
aws-sigv4 (1.5.2)
|
28
|
-
aws-eventstream (~> 1, >= 1.0.2)
|
29
|
-
bunny (2.20.3)
|
30
|
-
amq-protocol (~> 2.3, >= 2.3.1)
|
31
|
-
sorted_set (~> 1, >= 1.0.2)
|
32
10
|
diff-lcs (1.5.0)
|
33
|
-
|
11
|
+
docile (1.4.0)
|
34
12
|
json (2.6.3)
|
35
13
|
parallel (1.23.0)
|
36
14
|
parser (3.2.2.1)
|
37
15
|
ast (~> 2.4.1)
|
38
16
|
rainbow (3.1.1)
|
39
17
|
rake (13.0.6)
|
40
|
-
rbtree (0.4.6)
|
41
18
|
regexp_parser (2.8.0)
|
42
19
|
rexml (3.2.5)
|
43
20
|
rspec (3.12.0)
|
@@ -76,14 +53,17 @@ GEM
|
|
76
53
|
rubocop-capybara (~> 2.17)
|
77
54
|
rubocop-factory_bot (~> 2.22)
|
78
55
|
ruby-progressbar (1.13.0)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
56
|
+
simplecov (0.22.0)
|
57
|
+
docile (~> 1.1)
|
58
|
+
simplecov-html (~> 0.11)
|
59
|
+
simplecov_json_formatter (~> 0.1)
|
60
|
+
simplecov-html (0.12.3)
|
61
|
+
simplecov_json_formatter (0.1.4)
|
83
62
|
unicode-display_width (2.4.2)
|
84
63
|
|
85
64
|
PLATFORMS
|
86
65
|
x86_64-darwin-21
|
66
|
+
x86_64-linux
|
87
67
|
|
88
68
|
DEPENDENCIES
|
89
69
|
event_hub!
|
@@ -92,6 +72,7 @@ DEPENDENCIES
|
|
92
72
|
rubocop (~> 1.21)
|
93
73
|
rubocop-rake
|
94
74
|
rubocop-rspec
|
75
|
+
simplecov
|
95
76
|
|
96
77
|
BUNDLED WITH
|
97
78
|
2.4.13
|
data/README.md
CHANGED
@@ -1,24 +1,144 @@
|
|
1
1
|
# EventHub
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
This library structurizes the application code for inter microservice communication.
|
4
|
+
Each micro-service has their own queue. All those queues are bind to the single exchange. So all the
|
5
|
+
micro-services can publish events to that exchange and only the events that are needed will get into
|
6
|
+
the corresponding micro-service's queue.
|
6
7
|
|
7
8
|
## Installation
|
8
9
|
|
9
|
-
|
10
|
+
You don't need to specify this gem directly in your Gemfile. Instead you need to add one of (or many)
|
11
|
+
"adapter" gems like `event_hub_aws` for AWS or `event_hub_bunny` for RabbitMQ. This library will be added
|
12
|
+
as a dependency.
|
13
|
+
|
14
|
+
## Configuration
|
15
|
+
|
16
|
+
Create a config file where you should specify events you need in this specific app. In the subscribe
|
17
|
+
section specify events you wanna receive and the handlers.
|
18
|
+
|
19
|
+
```yaml
|
20
|
+
# config/event_hub.yml
|
21
|
+
|
22
|
+
development:
|
23
|
+
queue: my-micro-service-events
|
24
|
+
exchange: event-hub
|
25
|
+
adapter: Bunny
|
26
|
+
subscribe:
|
27
|
+
user_registered:
|
28
|
+
handler: Handlers::UserRegistered
|
29
|
+
```
|
30
|
+
|
31
|
+
Then apply this config:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# config/initializers/event_hub.rb
|
35
|
+
|
36
|
+
config = Rails.application.config_for(:event_hub)
|
37
|
+
|
38
|
+
# This block will be called in case of problems during the event handling
|
39
|
+
config[:on_failure] = lambda do |e, _message|
|
40
|
+
raise(e) if Rails.env.test?
|
41
|
+
# notify developers about the problem
|
42
|
+
end
|
43
|
+
|
44
|
+
EventHub.configure(config)
|
45
|
+
```
|
46
|
+
|
47
|
+
You need to implement the `event` and `event handler`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# app/event_hub/events/user_registered.rb
|
51
|
+
class Events::TickerUpserted < EventHub::Event
|
52
|
+
event :user_registered
|
53
|
+
version '1.1'
|
54
|
+
|
55
|
+
attribute :id
|
56
|
+
attribute :email
|
57
|
+
attribute :name
|
58
|
+
end
|
59
|
+
|
60
|
+
# app/event_hub/handlers/user_registered.rb
|
61
|
+
class Handlers::UserRegistered < EventHub::Handler
|
62
|
+
version '1.1'
|
63
|
+
|
64
|
+
def call
|
65
|
+
User.create(event.as_json)
|
66
|
+
end
|
67
|
+
|
68
|
+
# this method will be called in case if the received event version isn't eql to the handler version
|
69
|
+
def on_incorrect_version
|
70
|
+
event_major, event_minor = message.version.split('.')
|
71
|
+
handler_major, handler_minor = self.class.version.split('.')
|
72
|
+
|
73
|
+
if event_major != handler_major || event_minor < handler_minor
|
74
|
+
# TODO: notify rollbar
|
75
|
+
raise IgnoreMessage
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def event
|
82
|
+
@event ||= Events::UserRegistered.new(JSON.parse(message.body))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
To bind the exchange to the queue run:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
EventHub.adapter.setup_bindings
|
91
|
+
```
|
92
|
+
You need to run this command each time you change the `subscription` part of the config file.
|
93
|
+
You can create a migration with `EventHub.adapter.setup_bindings` in your app each time you
|
94
|
+
need to update bindings.
|
95
|
+
|
96
|
+
### Listen to events
|
97
|
+
|
98
|
+
To receive events from other system you need to run a daemon process that will call blocking method:
|
99
|
+
```ruby
|
100
|
+
EventHub.subscribe
|
101
|
+
```
|
102
|
+
This method listens for events and call corresponding handlers.
|
103
|
+
|
104
|
+
### Event publishing
|
105
|
+
|
106
|
+
To publish an event you need to create its instance and call its `publish` method.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Events::UserRegistered.new(id: user.id, email: user.email).publish
|
110
|
+
```
|
111
|
+
|
112
|
+
## Event versions
|
113
|
+
|
114
|
+
The event routing is done based on the event name. So if you want let's say to add an attribute to the event
|
115
|
+
you must upgrade its version. We recommend two-level version structure.
|
10
116
|
|
11
|
-
|
117
|
+
If the event change breaks the contract the published should publish both versions of the event for some time.
|
118
|
+
For the new event the major part of the version must be changed.
|
119
|
+
The subscriber should reject the unsuported version and notify the responsible developers. It can be done in
|
120
|
+
`on_incorrect_version` handler method. So the micro-service's development team will have time to react onto the
|
121
|
+
problem and update the handler.
|
122
|
+
|
123
|
+
If the event change just extends the contract then the minor version should be changed. The subscriber should
|
124
|
+
notify the development team but still handle the event.
|
12
125
|
|
13
|
-
|
126
|
+
## Race conditions
|
14
127
|
|
15
|
-
|
128
|
+
It can happen that the older event will be processed later and overrides the previous event changes. It especially
|
129
|
+
can happen if you use several listener daemons. But still if you use a single daemon you are not protected because
|
130
|
+
the event can get back into the queue and change the order.
|
16
131
|
|
17
|
-
|
132
|
+
As a solution you can add a timestamp to the event and on the subscriber side add a field to the model you are
|
133
|
+
going to update. Each time you receive a new event you should check that the event timestamp is newer that the
|
134
|
+
timestamp in the DB. In this case you can be sure that you've received the latest event.
|
18
135
|
|
19
|
-
|
136
|
+
There are still can be a problem with the above approach. It can happen that the publisher is run on several
|
137
|
+
servers and they have a small difference in their clock. If you have so frequently updated models then probably
|
138
|
+
you need to use Redis counter to generate the event "update version".
|
20
139
|
|
21
|
-
|
140
|
+
On the subscriber side you can use Redlock to prevent race conditions. But pay attention that Redis adds lag to
|
141
|
+
the communication so you don't need it for all the models.
|
22
142
|
|
23
143
|
## Development
|
24
144
|
|
data/event_hub.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ['biguban@gmail.com']
|
10
10
|
|
11
11
|
spec.summary = 'Event driven inter microservice communication'
|
12
|
-
spec.description = '
|
12
|
+
spec.description = 'This library structurizes the code for inter microservice communication using events'
|
13
13
|
spec.homepage = 'https://github.com/seekingalpha/event_hub'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
spec.required_ruby_version = '>= 2.6.0'
|
@@ -31,11 +31,6 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
32
|
spec.require_paths = ['lib']
|
33
33
|
|
34
|
-
# Uncomment to register a new dependency of your gem
|
35
|
-
spec.add_dependency 'aws-sdk-sns', '~> 1.0'
|
36
|
-
spec.add_dependency 'aws-sdk-sqs', '~> 1.0'
|
37
|
-
spec.add_dependency 'bunny', '>= 2.13.0'
|
38
|
-
|
39
34
|
# For more information and examples about making a new gem, check out our
|
40
35
|
# guide at: https://bundler.io/guides/creating_gem.html
|
41
36
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
@@ -1,28 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class EventHub
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
class EventHub
|
4
|
+
module Adapters
|
5
|
+
class Test
|
6
|
+
class Message < EventHub::Message
|
7
|
+
def initialize(event, queue, attributes = {})
|
8
|
+
@event = event
|
9
|
+
@body = event.body
|
10
|
+
@attributes = attributes
|
11
|
+
@queue = queue
|
12
|
+
end
|
9
13
|
|
10
|
-
|
14
|
+
attr_reader :attributes, :body
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def event
|
17
|
+
@event.class.event
|
18
|
+
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
def version
|
21
|
+
@event.class.version
|
22
|
+
end
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
def ack
|
25
|
+
@ack = true
|
26
|
+
@queue.delete(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def reject
|
30
|
+
@rejected = true
|
31
|
+
@queue.delete(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def ack?
|
35
|
+
!!@ack
|
36
|
+
end
|
24
37
|
|
25
|
-
|
26
|
-
|
38
|
+
def rejected?
|
39
|
+
!!@rejected
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
27
43
|
end
|
28
44
|
end
|
@@ -1,24 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class EventHub
|
4
|
-
|
3
|
+
class EventHub
|
4
|
+
module Adapters
|
5
|
+
class Test
|
6
|
+
attr_accessor :queue
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@queue = []
|
11
|
+
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def subscribe(&block)
|
14
|
+
@queue.each do |message|
|
15
|
+
block.call(message)
|
16
|
+
end
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
def publish(event)
|
20
|
+
@queue << Message.new(event, @queue)
|
21
|
+
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
def setup_bindings
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
data/lib/event_hub/adapters.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module EventHub::Adapters; end
|
4
|
-
require_relative 'adapters/bunny'
|
5
|
-
require_relative 'adapters/bunny/message'
|
6
|
-
require_relative 'adapters/aws'
|
7
|
-
require_relative 'adapters/aws/message'
|
8
3
|
require_relative 'adapters/test'
|
9
4
|
require_relative 'adapters/test/message'
|
data/lib/event_hub/event.rb
CHANGED
@@ -1,27 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
def publish
|
5
|
-
EventHub.publish(self)
|
6
|
-
end
|
3
|
+
require 'json'
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class EventHub
|
6
|
+
class Event
|
7
|
+
def initialize(hash = {})
|
8
|
+
hash.transform_keys(&:to_s).slice(*self.class.attributes.keys).each do |attr, val|
|
9
|
+
public_send("#{attr}=", val)
|
10
|
+
end
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
13
|
+
def publish
|
14
|
+
EventHub.publish(self)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def body
|
18
|
+
self.class.attributes.keys.to_h do |attr|
|
19
|
+
[attr, public_send(attr)]
|
20
|
+
end.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.event(event = nil)
|
24
|
+
@event = event.to_s if event
|
25
|
+
@event
|
26
|
+
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
def self.version(version = nil)
|
29
|
+
@version = version if version
|
30
|
+
@version
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_reader :attributes
|
35
|
+
|
36
|
+
def attribute(attribute, options = {})
|
37
|
+
@attributes ||= {}
|
38
|
+
@attributes[attribute.to_s] = options
|
39
|
+
|
40
|
+
attr_accessor(attribute)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
27
44
|
end
|
data/lib/event_hub/handler.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class EventHub
|
4
|
-
|
3
|
+
class EventHub
|
4
|
+
class Handler
|
5
|
+
attr_reader :message
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def self.version(version = nil)
|
8
|
+
@version = version if version
|
9
|
+
@version
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def initialize(message)
|
13
|
+
@message = message
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
def on_incorrect_version
|
17
|
+
raise IncorrectVersion
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
def validate!
|
21
|
+
on_incorrect_version if @message.version != self.class.version
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/event_hub/message.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class EventHub
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class EventHub
|
4
|
+
class Message
|
5
|
+
def body
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def routing_key
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def headers
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
# Message acknowledgment
|
18
|
+
def ack
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
# moves message to the dead queue
|
23
|
+
def reject
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
data/lib/event_hub/version.rb
CHANGED
data/lib/event_hub.rb
CHANGED
@@ -13,6 +13,10 @@ class EventHub
|
|
13
13
|
class RejectMessage < StandardError; end
|
14
14
|
|
15
15
|
def self.configure(config)
|
16
|
+
if !config[:on_failure] || !config[:on_failure].respond_to?(:call) || config[:on_failure].arity != 2
|
17
|
+
raise ArgumentError, 'EventHub configuration must have `on_failure` callable options with arity == 2'
|
18
|
+
end
|
19
|
+
|
16
20
|
@config = config
|
17
21
|
@instance = nil
|
18
22
|
end
|
@@ -52,10 +56,10 @@ class EventHub
|
|
52
56
|
def initialize(config)
|
53
57
|
@config = config
|
54
58
|
@config[:subscribe] ||= {}
|
55
|
-
@config[:subscribe].each_value { |subscription| subscription[:handler] = subscription[:handler]
|
59
|
+
@config[:subscribe].each_value { |subscription| subscription[:handler] = Object.const_get(subscription[:handler]) }
|
56
60
|
end
|
57
61
|
|
58
62
|
def adapter
|
59
|
-
@adapter ||= Adapters.const_get(@config[:adapter]
|
63
|
+
@adapter ||= Adapters.const_get(@config[:adapter]).new(@config)
|
60
64
|
end
|
61
65
|
end
|
metadata
CHANGED
@@ -1,58 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: event_hub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bogdan Guban
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: aws-sdk-sqs
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bunny
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 2.13.0
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 2.13.0
|
55
|
-
description: Event driven inter microservice communication
|
11
|
+
date: 2023-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This library structurizes the code for inter microservice communication
|
14
|
+
using events
|
56
15
|
email:
|
57
16
|
- biguban@gmail.com
|
58
17
|
executables: []
|
@@ -71,10 +30,6 @@ files:
|
|
71
30
|
- event_hub.gemspec
|
72
31
|
- lib/event_hub.rb
|
73
32
|
- lib/event_hub/adapters.rb
|
74
|
-
- lib/event_hub/adapters/aws.rb
|
75
|
-
- lib/event_hub/adapters/aws/message.rb
|
76
|
-
- lib/event_hub/adapters/bunny.rb
|
77
|
-
- lib/event_hub/adapters/bunny/message.rb
|
78
33
|
- lib/event_hub/adapters/test.rb
|
79
34
|
- lib/event_hub/adapters/test/message.rb
|
80
35
|
- lib/event_hub/event.rb
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class EventHub::Adapters::Aws::Message < EventHub::Message
|
4
|
-
def initialize(adapter, message)
|
5
|
-
@adapter = adapter
|
6
|
-
@message = message
|
7
|
-
end
|
8
|
-
|
9
|
-
def attributes
|
10
|
-
@attributes ||= parsed_body['MessageAttributes'].transform_values { |v| v['Value'] }
|
11
|
-
end
|
12
|
-
|
13
|
-
def body
|
14
|
-
parsed_body['Message']
|
15
|
-
end
|
16
|
-
|
17
|
-
def event
|
18
|
-
attributes['event']
|
19
|
-
end
|
20
|
-
|
21
|
-
def version
|
22
|
-
attributes['version']
|
23
|
-
end
|
24
|
-
|
25
|
-
def ack
|
26
|
-
# Delete the message from the queue.
|
27
|
-
@adapter.delete_message(@message.receipt_handle)
|
28
|
-
end
|
29
|
-
|
30
|
-
def reject
|
31
|
-
if @adapter.config[:dead_queue_url]
|
32
|
-
# TODO: publish the message to the dead_queue
|
33
|
-
end
|
34
|
-
ack
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
# {
|
40
|
-
# "Type" : "Notification",
|
41
|
-
# "MessageId" : "025727dd-bb27-5c53-8a79-7b38f0cfe19a",
|
42
|
-
# "SequenceNumber" : "10000000000000020000",
|
43
|
-
# "TopicArn" : "arn:aws:sns:us-west-2:744522205193:event-hub.fifo",
|
44
|
-
# "Subject" : "subject",
|
45
|
-
# "Message" : "body",
|
46
|
-
# "Timestamp" : "2023-05-22T10:53:28.806Z",
|
47
|
-
# "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:744522205193:event-hub.fifo:c8e1a595-4b0d-44b4-8087-094e7b0de09a",
|
48
|
-
# "MessageAttributes" : {
|
49
|
-
# "str_attr" : {"Type":"String","Value":"string_attr_val"}
|
50
|
-
# }
|
51
|
-
# }
|
52
|
-
def parsed_body
|
53
|
-
@parsed_body ||= JSON.parse(@message.body)
|
54
|
-
end
|
55
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'aws-sdk-sns'
|
4
|
-
require 'aws-sdk-sqs'
|
5
|
-
|
6
|
-
class EventHub::Adapters::Aws
|
7
|
-
attr_reader :config
|
8
|
-
|
9
|
-
def initialize(config)
|
10
|
-
@config = config
|
11
|
-
end
|
12
|
-
|
13
|
-
def subscribe(&block)
|
14
|
-
loop do
|
15
|
-
receive_message_result = sqs.receive_message({
|
16
|
-
queue_url: @config[:queue_url],
|
17
|
-
message_attribute_names: ['All'], # Receive all custom attributes.
|
18
|
-
max_number_of_messages: 10, # Receive at most one message.
|
19
|
-
wait_time_seconds: 15 # Do not wait to check for the message.
|
20
|
-
})
|
21
|
-
|
22
|
-
# Display information about the message.
|
23
|
-
# Display the message's body and each custom attribute value.
|
24
|
-
receive_message_result.messages.each do |aws_msg|
|
25
|
-
message = Message.new(self, aws_msg)
|
26
|
-
block.call(message)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def publish(event)
|
32
|
-
topic.publish({
|
33
|
-
message: event.to_json,
|
34
|
-
message_attributes: {
|
35
|
-
event: { data_type: 'String', string_value: event.class.event },
|
36
|
-
version: { data_type: 'String', string_value: event.class.version },
|
37
|
-
},
|
38
|
-
message_group_id: 'message_group_id',
|
39
|
-
message_deduplication_id: SecureRandom.uuid
|
40
|
-
})
|
41
|
-
end
|
42
|
-
|
43
|
-
def setup_bindings
|
44
|
-
policy = { event: @config[:subscribe].keys }.to_json
|
45
|
-
subscription = topic.subscriptions.find { |s| s.attributes['Endpoint'] == @config[:queue_arn] }
|
46
|
-
if subscription
|
47
|
-
subscription.set_attributes({
|
48
|
-
attribute_name: 'FilterPolicy',
|
49
|
-
attribute_value: policy
|
50
|
-
})
|
51
|
-
else
|
52
|
-
topic.subscribe({
|
53
|
-
protocol: 'sqs',
|
54
|
-
attributes: { 'FilterPolicy' => policy },
|
55
|
-
endpoint: @config[:queue_arn]
|
56
|
-
})
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def delete_message(receipt_handle)
|
61
|
-
sqs.delete_message(
|
62
|
-
queue_url: @config[:queue_url],
|
63
|
-
receipt_handle: receipt_handle
|
64
|
-
)
|
65
|
-
end
|
66
|
-
|
67
|
-
def topic
|
68
|
-
@topic ||= sns.topic(@config[:exchange_arn])
|
69
|
-
end
|
70
|
-
|
71
|
-
def sns
|
72
|
-
@sns ||= Aws::SNS::Resource.new(@config[:credentials] || {})
|
73
|
-
end
|
74
|
-
|
75
|
-
def sqs
|
76
|
-
@sqs ||= Aws::SQS::Client.new(@config[:credentials] || {})
|
77
|
-
end
|
78
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class EventHub::Adapters::Bunny::Message < EventHub::Message
|
4
|
-
def initialize(delivery_info, properties, body, channel)
|
5
|
-
@delivery_info = delivery_info
|
6
|
-
@properties = properties
|
7
|
-
@body = body
|
8
|
-
@channel = channel
|
9
|
-
end
|
10
|
-
|
11
|
-
def attributes
|
12
|
-
@properties
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :body
|
16
|
-
|
17
|
-
def event
|
18
|
-
@properties[:event]
|
19
|
-
end
|
20
|
-
|
21
|
-
def version
|
22
|
-
@properties[:version]
|
23
|
-
end
|
24
|
-
|
25
|
-
def ack
|
26
|
-
@channel.ack(@delivery_info.delivery_tag)
|
27
|
-
end
|
28
|
-
|
29
|
-
def reject
|
30
|
-
@channel.reject(@delivery_info.delivery_tag)
|
31
|
-
end
|
32
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bunny'
|
4
|
-
|
5
|
-
class EventHub::Adapters::Bunny
|
6
|
-
def initialize(config)
|
7
|
-
@config = config
|
8
|
-
end
|
9
|
-
|
10
|
-
def subscribe(&block)
|
11
|
-
channel.prefetch(1)
|
12
|
-
queue.subscribe(block: true, manual_ack: true) do |delivery_info, properties, body|
|
13
|
-
message = Message.new(delivery_info, properties, body, channel)
|
14
|
-
block.call(message)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def publish(event)
|
19
|
-
exchange.publish(
|
20
|
-
event.body,
|
21
|
-
routing_key: event.class.event,
|
22
|
-
persistent: true,
|
23
|
-
event: event.class.event,
|
24
|
-
version: event.class.version
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
def setup_bindings
|
29
|
-
@config.subscribe.each_key do |routing_key|
|
30
|
-
queue.bind(exchange, routing_key: routing_key)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def channel
|
35
|
-
@channel ||= connection.create_channel
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def exchange
|
41
|
-
@exchange ||= channel.direct(@config[:exchange])
|
42
|
-
end
|
43
|
-
|
44
|
-
def queue
|
45
|
-
@queue ||= channel.queue(@config[:queue], durable: true)
|
46
|
-
end
|
47
|
-
|
48
|
-
def connection
|
49
|
-
return @connection if defined?(@connection)
|
50
|
-
|
51
|
-
@connection = ::Bunny.new # TODO: config
|
52
|
-
@connection.start
|
53
|
-
@connection
|
54
|
-
end
|
55
|
-
end
|