event_hub 0.1.0 → 1.0.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: f88590e890094b75995c444528f90a10ba26df7573927c6cbfd3a1db609a3c65
4
+ data.tar.gz: 6b820bf00fe54147b2c4067f3bfb43a50d77b59f9c8e047939188fc761d6a646
5
5
  SHA512:
6
- metadata.gz: d5817202591324e0be2e21b0f0cb7a23ebc7ec3a08cc1fb01f63b97b476a2a868190eacfb51f7a18cc6dd99f791668cfaeafcf5a6daada3b0dc64878bf1dbbec
7
- data.tar.gz: 2fc8f07f0dca34b4037a44fdf010bf340fc3c413240620c97ba08ff2da5a66e453b0d1086bc9aaf912261435873fd01f102a486292015de2592d1c0f74c63fed
6
+ metadata.gz: 906008f796563c57fe06c883ce24323aad1072110a11525ff35da682e4912f02ba336c737411c2999ccdd97b1ea11f58859f21fba478e342c1eab600c9c32b21
7
+ data.tar.gz: 391b241d8c1957f8a4f27d9f12daf060a50fccf34a5630be4b711b62d60fabcc8c1f3323720d30082d811401a7644d35259d227bc1abb00ec9221b073a3c9c84
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,20 @@
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.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
- jmespath (1.6.2)
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
- set (1.0.3)
80
- sorted_set (1.0.3)
81
- rbtree
82
- set (~> 1.0)
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
- 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
+ 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
- Install the gem and add to the application's Gemfile by executing:
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
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
126
+ ## Race conditions
14
127
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
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
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
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
- ## Usage
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
- TODO: Write usage instructions here
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 = '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,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::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,44 @@
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
+ 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
- def self.event(event = nil)
13
- @event = event if event
14
- @event
15
- end
13
+ def publish
14
+ EventHub.publish(self)
15
+ end
16
16
 
17
- def self.version(version = nil)
18
- @version = version if version
19
- @version
20
- end
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
- # def self.field(field)
23
- # self.fields ||= Set.new
24
- # self.fields << field
25
- # attr_accessor(field)
26
- # end
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
@@ -1,22 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventHub::Handler
4
- attr_reader :message
3
+ class EventHub
4
+ class Handler
5
+ attr_reader :message
5
6
 
6
- def self.version(version = nil)
7
- @version = version if version
8
- @version
9
- end
7
+ def self.version(version = nil)
8
+ @version = version if version
9
+ @version
10
+ end
10
11
 
11
- def initialize(message)
12
- @message = message
13
- end
12
+ def initialize(message)
13
+ @message = message
14
+ end
14
15
 
15
- def on_incorrect_version
16
- raise IncorrectVersion
17
- end
16
+ def on_incorrect_version
17
+ raise IncorrectVersion
18
+ end
18
19
 
19
- def validate!
20
- on_incorrect_version if @message.version != self.class.version
20
+ def validate!
21
+ on_incorrect_version if @message.version != self.class.version
22
+ end
21
23
  end
22
24
  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.0.0'
5
5
  end
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].constantize }
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].camelize).new(@config)
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: 0.1.0
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-19 00:00:00.000000000 Z
12
- dependencies:
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
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