routemaster-client 2.0.0 → 2.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
  SHA1:
3
- metadata.gz: 1ee29c57b725f321cc404fba4ac361d7ba277d1a
4
- data.tar.gz: 707c11f90c92de25d0b7a40ed6b452e61b2016af
3
+ metadata.gz: 4ab7db72b3f45ca016af6ddfcd31727c08448d04
4
+ data.tar.gz: 864cc421186071bf194c01c230f778016ab4011c
5
5
  SHA512:
6
- metadata.gz: b63264e4bf56e517980d49716a4f7b4f5e7abfd0f8a0c6ccb0c95d22284e7ce13263182483ac9cac081e4f4fcc70c4fa7325c19d1660ac4aec47fea59c3ec195
7
- data.tar.gz: 8d67555f4b949ba7245340ac46a4ad85d9fb307a6f829755e783f20e83018db0d218aedd34db719998522009e787ca1b910fcb77ff01f7a1ad1a2703fed80df4
6
+ metadata.gz: d48921d988693488e7aed1b03b22526a88cd320eb85ce97976c32b0b0458fd79703043a0ef9f3e82971a9afcc3712f6427ce55c7aacc3ce5b00af81dd15ca24e
7
+ data.tar.gz: 856f4e0a2f340dbbe9d5c8b13e39fea9026b94ce48f53ec81bbca6da1ba5d860fd4460339cbd5fefdf3f43129cc51bc63130b81aa5e4132a2a84ee350bd34573
data/Gemfile CHANGED
@@ -15,4 +15,5 @@ gem 'pry-byebug'
15
15
  gem 'rack-test'
16
16
  gem 'simplecov'
17
17
  gem 'codeclimate-test-reporter', require: nil
18
+ gem 'sidekiq'
18
19
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- routemaster-client (2.0.0)
4
+ routemaster-client (2.1.0)
5
5
  faraday (>= 0.9.0)
6
6
  oj (~> 2.17)
7
7
  typhoeus (~> 1.1)
@@ -16,6 +16,8 @@ GEM
16
16
  codeclimate-test-reporter (1.0.3)
17
17
  simplecov
18
18
  coderay (1.1.1)
19
+ concurrent-ruby (1.0.2)
20
+ connection_pool (2.2.1)
19
21
  crack (0.4.3)
20
22
  safe_yaml (~> 1.0.0)
21
23
  diff-lcs (1.2.5)
@@ -64,12 +66,15 @@ GEM
64
66
  psych (2.2.1)
65
67
  public_suffix (2.0.4)
66
68
  rack (2.0.1)
69
+ rack-protection (1.5.3)
70
+ rack
67
71
  rack-test (0.6.3)
68
72
  rack (>= 1.0)
69
73
  rake (11.3.0)
70
74
  rb-fsevent (0.9.8)
71
75
  rb-inotify (0.9.7)
72
76
  ffi (>= 0.5.0)
77
+ redis (3.3.2)
73
78
  rspec (3.5.0)
74
79
  rspec-core (~> 3.5.0)
75
80
  rspec-expectations (~> 3.5.0)
@@ -86,6 +91,11 @@ GEM
86
91
  ruby_dep (1.5.0)
87
92
  safe_yaml (1.0.4)
88
93
  shellany (0.0.1)
94
+ sidekiq (4.2.1)
95
+ concurrent-ruby (~> 1.0)
96
+ connection_pool (~> 2.2, >= 2.2.0)
97
+ rack-protection (~> 1.5)
98
+ redis (~> 3.2, >= 3.2.1)
89
99
  simplecov (0.12.0)
90
100
  docile (~> 1.1.0)
91
101
  json (>= 1.8, < 3)
@@ -113,6 +123,7 @@ DEPENDENCIES
113
123
  rack-test
114
124
  rake
115
125
  routemaster-client!
126
+ sidekiq
116
127
  simplecov
117
128
  webmock
118
129
 
data/README.md CHANGED
@@ -34,6 +34,11 @@ You can also specify a timeout value in seconds if you like with the ```timeout`
34
34
  Routemaster::Client.new(url: 'https://bus.example.com', uuid: 'demo', timeout: 2)
35
35
  ```
36
36
 
37
+ If you are using Sidekiq in your project, you can specify the usage of a Sidekiq backend, where event sending will be processed asynchronously.
38
+
39
+ ```ruby
40
+ Routemaster::Client.new(url, 'https://bus.example.com', uuid: 'demo', backend_type: Routemaster::Client::Backends::Sidekiq)
41
+ ```
37
42
 
38
43
  **Push** an event about an entity in the topic `widgets` with a callback URL:
39
44
 
@@ -0,0 +1,24 @@
1
+ require 'routemaster/client/connection'
2
+
3
+ module Routemaster
4
+ class Client
5
+ module Backends
6
+ class Sidekiq
7
+ class Worker
8
+ include ::Sidekiq::Worker
9
+
10
+ def perform(event, topic, callback, timestamp, options)
11
+ conn = Routemaster::Client::Connection.new(_symbolize_keys(options))
12
+ conn.send_event(event, topic, callback, timestamp)
13
+ end
14
+
15
+ private
16
+
17
+ def _symbolize_keys(hash)
18
+ Hash[hash.map{|(k,v)| [k.to_sym,v]}]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require 'sidekiq'
2
+ require 'routemaster/client/backends/sidekiq/worker'
3
+
4
+ module Routemaster
5
+ class Client
6
+ module Backends
7
+ class Sidekiq
8
+ @queue = :realtime
9
+
10
+ class << self
11
+ def configure(options)
12
+ new(options)
13
+ end
14
+ end
15
+
16
+ def initialize(options)
17
+ @_options = options
18
+ end
19
+
20
+ def send_event(event, topic, callback, timestamp = nil)
21
+ Worker.perform_async(event, topic, callback, timestamp, @_options)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ require 'routemaster/client/connection'
2
+
3
+ module Routemaster
4
+ class Client
5
+ module Backends
6
+ class Synchronous
7
+
8
+ class << self
9
+ def configure(options)
10
+ new(options)
11
+ end
12
+ end
13
+
14
+ def initialize(options)
15
+ @conn = Routemaster::Client::Connection.new(options)
16
+ end
17
+
18
+ def send_event(event, topic, callback, timestamp = nil)
19
+ @conn.send_event(event, topic, callback, timestamp)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ module Routemaster
2
+ class Client
3
+ module Backends
4
+ NAMES = ["Routemaster::Client::Backends::Synchronous", "Routemaster::Client::Backends::Sidekiq"]
5
+
6
+ autoload 'Synchronous', 'routemaster/client/backends/synchronous'
7
+ autoload 'Sidekiq', 'routemaster/client/backends/sidekiq'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,75 @@
1
+ require 'forwardable'
2
+
3
+ # Internal class used by Routemaster::Client
4
+
5
+ module Routemaster
6
+ class Client
7
+ class Connection
8
+
9
+ def initialize(options)
10
+ @_url = options[:url]
11
+ @_uuid = options[:uuid]
12
+ @_timeout = options.fetch(:timeout, 1)
13
+ @_verify_ssl = options.fetch(:verify_ssl, true)
14
+ end
15
+
16
+ def http(method, path, &block)
17
+ _conn.send(method, path, &block)
18
+ end
19
+
20
+ def post(path, &block)
21
+ http(:post, path, &block)
22
+ end
23
+
24
+ def get(path, &block)
25
+ http(:get, path, &block)
26
+ end
27
+
28
+ def delete(path, &block)
29
+ http(:delete, path, &block)
30
+ end
31
+
32
+ def send_event(event, topic, callback, timestamp = nil)
33
+ data = { type: event, url: callback, timestamp: timestamp }
34
+
35
+ response = post("/topics/#{topic}") do |r|
36
+ r.headers['Content-Type'] = 'application/json'
37
+ r.body = Oj.dump(_stringify_keys data)
38
+ end
39
+ fail "event rejected (#{response.status})" unless response.success?
40
+ end
41
+
42
+ def subscribe(options)
43
+ response = post('/subscription') do |r|
44
+ r.headers['Content-Type'] = 'application/json'
45
+ r.body = Oj.dump(_stringify_keys options)
46
+ end
47
+
48
+ unless response.success?
49
+ raise 'subscribe rejected'
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def _stringify_keys(hash)
56
+ hash.dup.tap do |h|
57
+ h.keys.each do |k|
58
+ h[k.to_s] = h.delete(k)
59
+ end
60
+ end
61
+ end
62
+
63
+ def _conn
64
+ @_conn ||= Faraday.new(@_url, ssl: { verify: @_verify_ssl }) do |f|
65
+ f.request :retry, max: 2, interval: 100e-3, backoff_factor: 2
66
+ f.request :basic_auth, @_uuid, 'x'
67
+ f.adapter :typhoeus
68
+
69
+ f.options.timeout = @_timeout
70
+ f.options.open_timeout = @_timeout
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,5 @@
1
1
  module Routemaster
2
2
  class Client
3
- VERSION = '2.0.0'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -1,3 +1,5 @@
1
+ require 'routemaster/client/backends'
2
+ require 'routemaster/client/connection'
1
3
  require 'routemaster/client/version'
2
4
  require 'routemaster/topic'
3
5
  require 'uri'
@@ -9,17 +11,22 @@ require 'oj'
9
11
 
10
12
  module Routemaster
11
13
  class Client
12
-
14
+
13
15
  def initialize(options = {})
14
- @_url = _assert_valid_url(options[:url])
15
- @_uuid = options[:uuid]
16
- @_timeout = options.fetch(:timeout, 1)
17
- @_verify_ssl = options.fetch(:verify_ssl, true)
16
+ @_options = options.tap do |o|
17
+ o[:timeout] ||= 1
18
+ o[:backend_type] ||= Backends::Synchronous
19
+ end
20
+
21
+ @_backend_type = @_options.fetch(:backend_type)
22
+
23
+ _assert_valid_url(@_options[:url])
24
+ _assert_valid_backend_type(@_backend_type)
25
+ _assert (@_options[:uuid] =~ /^[a-z0-9_-]{1,64}$/), 'uuid should be alpha'
26
+ _assert_valid_timeout(@_options[:timeout])
18
27
 
19
- _assert (options[:uuid] =~ /^[a-z0-9_-]{1,64}$/), 'uuid should be alpha'
20
- _assert_valid_timeout(@_timeout)
21
28
 
22
- unless options[:lazy]
29
+ unless @_options[:lazy]
23
30
  _conn.get('/pulse').tap do |response|
24
31
  raise 'cannot connect to bus' unless response.success?
25
32
  end
@@ -53,22 +60,14 @@ module Routemaster
53
60
 
54
61
  options[:topics].each { |t| _assert_valid_topic(t) }
55
62
  _assert_valid_url(options[:callback])
56
-
57
- response = _post('/subscription') do |r|
58
- r.headers['Content-Type'] = 'application/json'
59
- r.body = Oj.dump(_stringify_keys options)
60
- end
61
-
62
- unless response.success?
63
- raise 'subscribe rejected'
64
- end
63
+ _conn.subscribe(options)
65
64
  end
66
65
 
67
66
  def unsubscribe(*topics)
68
67
  topics.each { |t| _assert_valid_topic(t) }
69
68
 
70
69
  topics.each do |t|
71
- response = _delete("/subscriber/topics/#{t}")
70
+ response = _conn.delete("/subscriber/topics/#{t}")
72
71
 
73
72
  unless response.success?
74
73
  raise 'unsubscribe rejected'
@@ -77,7 +76,7 @@ module Routemaster
77
76
  end
78
77
 
79
78
  def unsubscribe_all
80
- response = _delete('/subscriber')
79
+ response = _conn.delete('/subscriber')
81
80
 
82
81
  unless response.success?
83
82
  raise 'unsubscribe all rejected'
@@ -87,7 +86,7 @@ module Routemaster
87
86
  def delete_topic(topic)
88
87
  _assert_valid_topic(topic)
89
88
 
90
- response = _delete("/topics/#{topic}")
89
+ response = _conn.delete("/topics/#{topic}")
91
90
 
92
91
  unless response.success?
93
92
  raise 'failed to delete topic'
@@ -95,7 +94,7 @@ module Routemaster
95
94
  end
96
95
 
97
96
  def monitor_topics
98
- response = _get('/topics') do |r|
97
+ response = _conn.get('/topics') do |r|
99
98
  r.headers['Content-Type'] = 'application/json'
100
99
  end
101
100
 
@@ -108,17 +107,8 @@ module Routemaster
108
107
  end
109
108
  end
110
109
 
111
-
112
110
  private
113
111
 
114
- def _stringify_keys(hash)
115
- hash.dup.tap do |h|
116
- h.keys.each do |k|
117
- h[k.to_s] = h.delete(k)
118
- end
119
- end
120
- end
121
-
122
112
  def _assert_valid_timeout(timeout)
123
113
  _assert (0..3_600_000).include?(timeout), 'bad timeout'
124
114
  end
@@ -141,49 +131,29 @@ module Routemaster
141
131
  _assert timestamp.kind_of?(Integer), 'not an integer'
142
132
  end
143
133
 
134
+ def _assert_valid_backend_type(backend_type)
135
+ backends = Backends::NAMES
136
+ _assert backends.include?(backend_type.to_s), "unknown backend type, must be one of #{backends.map{ |w| "Routemaster::Backends::#{w}" }.join(", ")}"
137
+ backend_type
138
+ end
139
+
144
140
  def _send_event(event, topic, callback, timestamp = nil)
145
141
  _assert_valid_url(callback)
146
142
  _assert_valid_topic(topic)
147
143
  _assert_valid_timestamp(timestamp) if timestamp
148
-
149
- data = { type: event, url: callback, timestamp: timestamp }
150
-
151
- response = _post("/topics/#{topic}") do |r|
152
- r.headers['Content-Type'] = 'application/json'
153
- r.body = Oj.dump(_stringify_keys data)
154
- end
155
- fail "event rejected (#{response.status})" unless response.success?
144
+ _backend.send_event(event, topic, callback, timestamp)
156
145
  end
157
146
 
158
147
  def _assert(condition, message)
159
148
  condition or raise ArgumentError.new(message)
160
149
  end
161
150
 
162
- def _http(method, path, &block)
163
- _conn.send(method, path, &block)
164
- end
165
-
166
- def _post(path, &block)
167
- _http(:post, path, &block)
168
- end
169
-
170
- def _get(path, &block)
171
- _http(:get, path, &block)
172
- end
173
-
174
- def _delete(path, &block)
175
- _http(:delete, path, &block)
176
- end
177
-
178
151
  def _conn
179
- @_conn ||= Faraday.new(@_url, ssl: { verify: @_verify_ssl }) do |f|
180
- f.request :retry, max: 2, interval: 100e-3, backoff_factor: 2
181
- f.request :basic_auth, @_uuid, 'x'
182
- f.adapter :typhoeus
152
+ @_conn ||= Client::Connection.new(@_options)
153
+ end
183
154
 
184
- f.options.timeout = @_timeout
185
- f.options.open_timeout = @_timeout
186
- end
155
+ def _backend
156
+ @_worker ||= @_backend_type.configure(@_options)
187
157
  end
188
158
  end
189
159
  end
data/spec/client_spec.rb CHANGED
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require 'routemaster/client'
3
3
  require 'routemaster/topic'
4
4
  require 'webmock/rspec'
5
+ require 'sidekiq/testing'
5
6
 
6
7
  describe Routemaster::Client do
7
8
  let(:options) {{
@@ -39,6 +40,12 @@ describe Routemaster::Client do
39
40
  expect { subject }.to raise_error(ArgumentError)
40
41
  end
41
42
 
43
+ it 'fails with an invalid worker_type' do
44
+ Jeff = double
45
+ options[:backend_type] = Jeff
46
+ expect { subject }.to raise_error(ArgumentError)
47
+ end
48
+
42
49
  context 'when connection fails' do
43
50
  before do
44
51
  stub_request(:any, %r{^https://bus.example.com}).
@@ -167,24 +174,58 @@ describe Routemaster::Client do
167
174
  end
168
175
  end
169
176
 
170
- describe '#created' do
171
- let(:event) { 'created' }
172
- it_behaves_like 'an event sender'
173
- end
177
+ context "With no background worker specified" do
178
+ describe '#created' do
179
+ let(:event) { 'created' }
180
+ it_behaves_like 'an event sender'
181
+ end
174
182
 
175
- describe '#updated' do
176
- let(:event) { 'updated' }
177
- it_behaves_like 'an event sender'
178
- end
183
+ describe '#updated' do
184
+ let(:event) { 'updated' }
185
+ it_behaves_like 'an event sender'
186
+ end
187
+
188
+ describe '#deleted' do
189
+ let(:event) { 'deleted' }
190
+ it_behaves_like 'an event sender'
191
+ end
179
192
 
180
- describe '#deleted' do
181
- let(:event) { 'deleted' }
182
- it_behaves_like 'an event sender'
193
+ describe '#noop' do
194
+ let(:event) { 'noop' }
195
+ it_behaves_like 'an event sender'
196
+ end
183
197
  end
184
198
 
185
- describe '#noop' do
186
- let(:event) { 'noop' }
187
- it_behaves_like 'an event sender'
199
+ context "With the sidekiq back end" do
200
+ before do
201
+ options[:backend_type] = Routemaster::Client::Backends::Sidekiq
202
+ end
203
+
204
+ around do |example|
205
+ Sidekiq::Testing.inline! do
206
+ example.run
207
+ end
208
+ end
209
+
210
+ describe '#created' do
211
+ let(:event) { 'created' }
212
+ it_behaves_like 'an event sender'
213
+ end
214
+
215
+ describe '#updated' do
216
+ let(:event) { 'updated' }
217
+ it_behaves_like 'an event sender'
218
+ end
219
+
220
+ describe '#deleted' do
221
+ let(:event) { 'deleted' }
222
+ it_behaves_like 'an event sender'
223
+ end
224
+
225
+ describe '#noop' do
226
+ let(:event) { 'noop' }
227
+ it_behaves_like 'an event sender'
228
+ end
188
229
  end
189
230
 
190
231
  describe '#subscribe' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routemaster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien Letessier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-07 00:00:00.000000000 Z
11
+ date: 2016-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -86,6 +86,11 @@ files:
86
86
  - Rakefile
87
87
  - routemaster-client.gemspec
88
88
  - routemaster/client.rb
89
+ - routemaster/client/backends.rb
90
+ - routemaster/client/backends/sidekiq.rb
91
+ - routemaster/client/backends/sidekiq/worker.rb
92
+ - routemaster/client/backends/synchronous.rb
93
+ - routemaster/client/connection.rb
89
94
  - routemaster/client/version.rb
90
95
  - routemaster/receiver.rb
91
96
  - routemaster/topic.rb