event_hub 0.1.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d70dbc69d43c7d32c39cb0e2bd8706be5832aad878449181991ec7a445d961e
4
- data.tar.gz: 7cbcaa9b40d9769c9721340866e9752af59107fb3b927494113825627bfe2649
3
+ metadata.gz: 2b8210587ddae3d73a60defbaaf3453da073c4dadb5f22b9da124fefc6b48dba
4
+ data.tar.gz: cba4f17fdab807b5d51f23e32e5f6bd8e3850b044b80586360bf13d8e946e9e0
5
5
  SHA512:
6
- metadata.gz: d5817202591324e0be2e21b0f0cb7a23ebc7ec3a08cc1fb01f63b97b476a2a868190eacfb51f7a18cc6dd99f791668cfaeafcf5a6daada3b0dc64878bf1dbbec
7
- data.tar.gz: 2fc8f07f0dca34b4037a44fdf010bf340fc3c413240620c97ba08ff2da5a66e453b0d1086bc9aaf912261435873fd01f102a486292015de2592d1c0f74c63fed
6
+ metadata.gz: 87fd3579dca7a99ac0fd4f3ead3086e757fb43ebbac8a9fbfa8aa2ef8f71ef9d093f4029ba30bf80cee478fd866084f871f2cf448aeb7a851ba737bef74b1033
7
+ data.tar.gz: 1a829b392c1683f83dbc0e8451afe14582803815eaa55f4b8f075100706c236dd95da347c70f7e68aa55bed45501ae316464056dc04aa09f6a8af62c9a18d5b4
data/.rubocop.yml CHANGED
@@ -48,8 +48,5 @@ Lint/AmbiguousOperatorPrecedence:
48
48
  Bundler/OrderedGems:
49
49
  Enabled: false
50
50
 
51
- Style/ClassAndModuleChildren:
52
- Enabled: false
53
-
54
51
  Lint/MissingSuper:
55
52
  Enabled: false
data/Gemfile CHANGED
@@ -12,3 +12,4 @@ gem 'rspec', '~> 3.0'
12
12
  gem 'rubocop', '~> 1.21'
13
13
  gem 'rubocop-rake'
14
14
  gem 'rubocop-rspec'
15
+ gem 'simplecov', require: false, group: :test
data/Gemfile.lock CHANGED
@@ -1,43 +1,41 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- event_hub (0.1.0)
5
- aws-sdk-sns (~> 1.0)
6
- aws-sdk-sqs (~> 1.0)
7
- bunny (>= 2.13.0)
4
+ event_hub (1.1.0)
5
+ activesupport (>= 5.0)
8
6
 
9
7
  GEM
10
8
  remote: https://rubygems.org/
11
9
  specs:
12
- amq-protocol (2.3.2)
10
+ activesupport (7.1.1)
11
+ base64
12
+ bigdecimal
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ connection_pool (>= 2.2.5)
15
+ drb
16
+ i18n (>= 1.6, < 2)
17
+ minitest (>= 5.1)
18
+ mutex_m
19
+ tzinfo (~> 2.0)
13
20
  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)
21
+ base64 (0.1.1)
22
+ bigdecimal (3.1.4)
23
+ concurrent-ruby (1.2.2)
24
+ connection_pool (2.4.1)
32
25
  diff-lcs (1.5.0)
33
- jmespath (1.6.2)
26
+ docile (1.4.0)
27
+ drb (2.1.1)
28
+ ruby2_keywords
29
+ i18n (1.14.1)
30
+ concurrent-ruby (~> 1.0)
34
31
  json (2.6.3)
32
+ minitest (5.20.0)
33
+ mutex_m (0.1.2)
35
34
  parallel (1.23.0)
36
35
  parser (3.2.2.1)
37
36
  ast (~> 2.4.1)
38
37
  rainbow (3.1.1)
39
38
  rake (13.0.6)
40
- rbtree (0.4.6)
41
39
  regexp_parser (2.8.0)
42
40
  rexml (3.2.5)
43
41
  rspec (3.12.0)
@@ -76,14 +74,21 @@ GEM
76
74
  rubocop-capybara (~> 2.17)
77
75
  rubocop-factory_bot (~> 2.22)
78
76
  ruby-progressbar (1.13.0)
79
- set (1.0.3)
80
- sorted_set (1.0.3)
81
- rbtree
82
- set (~> 1.0)
77
+ ruby2_keywords (0.0.5)
78
+ simplecov (0.22.0)
79
+ docile (~> 1.1)
80
+ simplecov-html (~> 0.11)
81
+ simplecov_json_formatter (~> 0.1)
82
+ simplecov-html (0.12.3)
83
+ simplecov_json_formatter (0.1.4)
84
+ tzinfo (2.0.6)
85
+ concurrent-ruby (~> 1.0)
83
86
  unicode-display_width (2.4.2)
84
87
 
85
88
  PLATFORMS
86
89
  x86_64-darwin-21
90
+ x86_64-darwin-22
91
+ x86_64-linux
87
92
 
88
93
  DEPENDENCIES
89
94
  event_hub!
@@ -92,6 +97,7 @@ DEPENDENCIES
92
97
  rubocop (~> 1.21)
93
98
  rubocop-rake
94
99
  rubocop-rspec
100
+ simplecov
95
101
 
96
102
  BUNDLED WITH
97
103
  2.4.13
data/README.md CHANGED
@@ -1,24 +1,156 @@
1
1
  # EventHub
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/event_hub`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
12
-
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
14
-
15
- If bundler is not being used to manage dependencies, install the gem by executing:
16
-
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
18
-
19
- ## Usage
20
-
21
- TODO: Write usage instructions here
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
+ set_callback :publish, :after, :log
55
+
56
+ attribute :id
57
+ attribute :email
58
+ attribute :name
59
+
60
+ private
61
+
62
+ def log
63
+ LogStasher.warn(event: :publish_event, event_name: self.class.event, attrs: as_json)
64
+ end
65
+ end
66
+
67
+ # app/event_hub/handlers/user_registered.rb
68
+ class Handlers::UserRegistered < EventHub::Handler
69
+ version '1.1'
70
+ set_callback :handle, :before, :log
71
+
72
+ def call
73
+ User.create(event.as_json)
74
+ end
75
+
76
+ # this method will be called in case if the received event version isn't eql to the handler version
77
+ def on_incorrect_version
78
+ event_major, event_minor = message.version.split('.')
79
+ handler_major, handler_minor = self.class.version.split('.')
80
+
81
+ if event_major != handler_major || event_minor < handler_minor
82
+ # TODO: notify rollbar
83
+ raise IgnoreMessage
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def log
90
+ LogStasher.warn(event: :handle_event, event_name: @message.event, attrs: @message.attributes)
91
+ end
92
+
93
+ def event
94
+ @event ||= Events::UserRegistered.new(JSON.parse(message.body))
95
+ end
96
+ end
97
+ ```
98
+
99
+ To bind the exchange to the queue run:
100
+
101
+ ```ruby
102
+ EventHub.adapter.setup_bindings
103
+ ```
104
+ You need to run this command each time you change the `subscription` part of the config file.
105
+ You can create a migration with `EventHub.adapter.setup_bindings` in your app each time you
106
+ need to update bindings.
107
+
108
+ ### Listen to events
109
+
110
+ To receive events from other system you need to run a daemon process that will call blocking method:
111
+ ```ruby
112
+ EventHub.subscribe
113
+ ```
114
+ This method listens for events and call corresponding handlers.
115
+
116
+ ### Event publishing
117
+
118
+ To publish an event you need to create its instance and call its `publish` method.
119
+
120
+ ```ruby
121
+ Events::UserRegistered.new(id: user.id, email: user.email).publish
122
+ ```
123
+
124
+ ## Event versions
125
+
126
+ The event routing is done based on the event name. So if you want let's say to add an attribute to the event
127
+ you must upgrade its version. We recommend two-level version structure.
128
+
129
+ If the event change breaks the contract the published should publish both versions of the event for some time.
130
+ For the new event the major part of the version must be changed.
131
+ The subscriber should reject the unsuported version and notify the responsible developers. It can be done in
132
+ `on_incorrect_version` handler method. So the micro-service's development team will have time to react onto the
133
+ problem and update the handler.
134
+
135
+ If the event change just extends the contract then the minor version should be changed. The subscriber should
136
+ notify the development team but still handle the event.
137
+
138
+ ## Race conditions
139
+
140
+ It can happen that the older event will be processed later and overrides the previous event changes. It especially
141
+ can happen if you use several listener daemons. But still if you use a single daemon you are not protected because
142
+ the event can get back into the queue and change the order.
143
+
144
+ As a solution you can add a timestamp to the event and on the subscriber side add a field to the model you are
145
+ going to update. Each time you receive a new event you should check that the event timestamp is newer that the
146
+ timestamp in the DB. In this case you can be sure that you've received the latest event.
147
+
148
+ There are still can be a problem with the above approach. It can happen that the publisher is run on several
149
+ servers and they have a small difference in their clock. If you have so frequently updated models then probably
150
+ you need to use Redis counter to generate the event "update version".
151
+
152
+ On the subscriber side you can use Redlock to prevent race conditions. But pay attention that Redis adds lag to
153
+ the communication so you don't need it for all the models.
22
154
 
23
155
  ## Development
24
156
 
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 = 'Event driven inter microservice communication'
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,10 +31,7 @@ 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'
34
+ spec.add_dependency 'activesupport', '>= 5.0'
38
35
 
39
36
  # For more information and examples about making a new gem, check out our
40
37
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -1,28 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventHub::Adapters::Test::Message < EventHub::Message
4
- def initialize(body, queue, attributes = {})
5
- @body = body
6
- @attributes = attributes
7
- @queue = queue
8
- end
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
- attr_reader :attributes, :body
14
+ attr_reader :attributes, :body
11
15
 
12
- def event
13
- @attributes[:event]
14
- end
16
+ def event
17
+ @event.class.event
18
+ end
15
19
 
16
- def version
17
- @attributes[:version]
18
- end
20
+ def version
21
+ @event.class.version
22
+ end
19
23
 
20
- def ack
21
- @ack = true
22
- @queue.delete(self)
23
- end
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
- def ack?
26
- !!@ack
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::Adapters::Test
4
- attr_accessor :queue
3
+ class EventHub
4
+ module Adapters
5
+ class Test
6
+ attr_accessor :queue
5
7
 
6
- def initialize(config)
7
- @config = config
8
- @queue = []
9
- end
8
+ def initialize(config)
9
+ @config = config
10
+ @queue = []
11
+ end
10
12
 
11
- def subscribe(&block)
12
- @queue.each do |message|
13
- block.call(message)
14
- end
15
- end
13
+ def subscribe(&block)
14
+ @queue.each do |message|
15
+ block.call(message)
16
+ end
17
+ end
16
18
 
17
- def publish(event)
18
- @queue << Message.new(event, @queue)
19
- end
19
+ def publish(event)
20
+ @queue << Message.new(event, @queue)
21
+ end
20
22
 
21
- def setup_bindings
22
- true
23
+ def setup_bindings
24
+ true
25
+ end
26
+ end
23
27
  end
24
28
  end
@@ -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'
@@ -1,27 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventHub::Event
4
- def publish
5
- EventHub.publish(self)
6
- end
3
+ require 'json'
7
4
 
8
- def body
9
- to_json
10
- end
5
+ class EventHub
6
+ class Event
7
+ include ::ActiveSupport::Callbacks
8
+ define_callbacks :publish
11
9
 
12
- def self.event(event = nil)
13
- @event = event if event
14
- @event
15
- end
10
+ def initialize(hash = {})
11
+ hash.transform_keys(&:to_s).slice(*self.class.attributes.keys).each do |attr, val|
12
+ public_send("#{attr}=", val)
13
+ end
14
+ end
16
15
 
17
- def self.version(version = nil)
18
- @version = version if version
19
- @version
20
- end
16
+ def publish
17
+ run_callbacks :publish do
18
+ EventHub.publish(self)
19
+ end
20
+ end
21
+
22
+ def body
23
+ self.class.attributes.keys.to_h do |attr|
24
+ [attr, public_send(attr)]
25
+ end.to_json
26
+ end
21
27
 
22
- # def self.field(field)
23
- # self.fields ||= Set.new
24
- # self.fields << field
25
- # attr_accessor(field)
26
- # end
28
+ def self.event(event = nil)
29
+ @event = event.to_s if event
30
+ @event
31
+ end
32
+
33
+ def self.version(version = nil)
34
+ @version = version if version
35
+ @version
36
+ end
37
+
38
+ class << self
39
+ attr_reader :attributes
40
+
41
+ def attribute(attribute, options = {})
42
+ @attributes ||= {}
43
+ @attributes[attribute.to_s] = options
44
+
45
+ attr_accessor(attribute)
46
+ end
47
+ end
48
+ end
27
49
  end
@@ -1,22 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventHub::Handler
4
- attr_reader :message
3
+ class EventHub
4
+ class Handler
5
+ include ::ActiveSupport::Callbacks
6
+ define_callbacks :handle
5
7
 
6
- def self.version(version = nil)
7
- @version = version if version
8
- @version
9
- end
8
+ attr_reader :message
10
9
 
11
- def initialize(message)
12
- @message = message
13
- end
10
+ def handle
11
+ run_callbacks :handle do
12
+ validate!
13
+ call
14
+ @message.ack
15
+ end
16
+ end
14
17
 
15
- def on_incorrect_version
16
- raise IncorrectVersion
17
- end
18
+ def self.version(version = nil)
19
+ @version = version if version
20
+ @version
21
+ end
22
+
23
+ def initialize(message)
24
+ @message = message
25
+ end
26
+
27
+ def on_incorrect_version
28
+ raise IncorrectVersion
29
+ end
18
30
 
19
- def validate!
20
- on_incorrect_version if @message.version != self.class.version
31
+ def validate!
32
+ on_incorrect_version if @message.version != self.class.version
33
+ end
21
34
  end
22
35
  end
@@ -1,25 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventHub::Message
4
- def body
5
- raise NotImplementedError
6
- end
3
+ class EventHub
4
+ class Message
5
+ def body
6
+ raise NotImplementedError
7
+ end
7
8
 
8
- def routing_key
9
- raise NotImplementedError
10
- end
9
+ def routing_key
10
+ raise NotImplementedError
11
+ end
11
12
 
12
- def headers
13
- raise NotImplementedError
14
- end
13
+ def headers
14
+ raise NotImplementedError
15
+ end
15
16
 
16
- # Message acknowledgment
17
- def ack
18
- raise NotImplementedError
19
- end
17
+ # Message acknowledgment
18
+ def ack
19
+ raise NotImplementedError
20
+ end
20
21
 
21
- # moves message to the dead queue
22
- def reject
23
- raise NotImplementedError
22
+ # moves message to the dead queue
23
+ def reject
24
+ raise NotImplementedError
25
+ end
24
26
  end
25
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class EventHub
4
- VERSION = '0.1.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/event_hub.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/callbacks'
3
4
  require_relative 'event_hub/version'
4
5
  require_relative 'event_hub/event'
5
6
  require_relative 'event_hub/handler'
@@ -13,6 +14,10 @@ class EventHub
13
14
  class RejectMessage < StandardError; end
14
15
 
15
16
  def self.configure(config)
17
+ if !config[:on_failure] || !config[:on_failure].respond_to?(:call) || config[:on_failure].arity != 2
18
+ raise ArgumentError, 'EventHub configuration must have `on_failure` callable options with arity == 2'
19
+ end
20
+
16
21
  @config = config
17
22
  @instance = nil
18
23
  end
@@ -27,10 +32,7 @@ class EventHub
27
32
  raise NoHandlerDefined unless handler_class
28
33
 
29
34
  handler = handler_class.new(message)
30
- handler.validate!
31
-
32
- handler.call
33
- message.ack
35
+ handler.handle
34
36
  rescue IgnoreMessage
35
37
  message.ack
36
38
  rescue RejectMessage
@@ -52,10 +54,10 @@ class EventHub
52
54
  def initialize(config)
53
55
  @config = config
54
56
  @config[:subscribe] ||= {}
55
- @config[:subscribe].each_value { |subscription| subscription[:handler] = subscription[:handler].constantize }
57
+ @config[:subscribe].each_value { |subscription| subscription[:handler] = Object.const_get(subscription[:handler]) }
56
58
  end
57
59
 
58
60
  def adapter
59
- @adapter ||= Adapters.const_get(@config[:adapter].camelize).new(@config)
61
+ @adapter ||= Adapters.const_get(@config[:adapter]).new(@config)
60
62
  end
61
63
  end
metadata CHANGED
@@ -1,58 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_hub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.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-19 00:00:00.000000000 Z
11
+ date: 2023-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: aws-sdk-sns
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
14
+ name: activesupport
43
15
  requirement: !ruby/object:Gem::Requirement
44
16
  requirements:
45
17
  - - ">="
46
18
  - !ruby/object:Gem::Version
47
- version: 2.13.0
19
+ version: '5.0'
48
20
  type: :runtime
49
21
  prerelease: false
50
22
  version_requirements: !ruby/object:Gem::Requirement
51
23
  requirements:
52
24
  - - ">="
53
25
  - !ruby/object:Gem::Version
54
- version: 2.13.0
55
- description: Event driven inter microservice communication
26
+ version: '5.0'
27
+ description: This library structurizes the code for inter microservice communication
28
+ using events
56
29
  email:
57
30
  - biguban@gmail.com
58
31
  executables: []
@@ -71,10 +44,6 @@ files:
71
44
  - event_hub.gemspec
72
45
  - lib/event_hub.rb
73
46
  - 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
47
  - lib/event_hub/adapters/test.rb
79
48
  - lib/event_hub/adapters/test/message.rb
80
49
  - 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