routemaster-client 2.1.0 → 3.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.
@@ -1,24 +1,33 @@
1
1
  require 'sidekiq'
2
+ require 'forwardable'
2
3
  require 'routemaster/client/backends/sidekiq/worker'
4
+ require 'routemaster/client/backends/sidekiq/configuration'
3
5
 
4
6
  module Routemaster
5
- class Client
7
+ module Client
6
8
  module Backends
7
9
  class Sidekiq
8
- @queue = :realtime
9
-
10
10
  class << self
11
- def configure(options)
12
- new(options)
11
+ extend Forwardable
12
+
13
+ attr_reader :options
14
+
15
+ def configure
16
+ self.tap do
17
+ Configuration.configure do |c|
18
+ yield c
19
+ end
20
+ @options = {'class' => Worker}.merge Configuration.sidekiq_options
21
+ end
13
22
  end
14
- end
15
23
 
16
- def initialize(options)
17
- @_options = options
18
- end
24
+ def send_event(*args)
25
+ opts = @options.merge('args' => args)
26
+ ::Sidekiq::Client.push opts
19
27
 
20
- def send_event(event, topic, callback, timestamp = nil)
21
- Worker.perform_async(event, topic, callback, timestamp, @_options)
28
+ # The push will throw an exception if there is a problem
29
+ true
30
+ end
22
31
  end
23
32
  end
24
33
  end
@@ -0,0 +1,48 @@
1
+ module Routemaster
2
+ module Client
3
+ module Backends
4
+ class Sidekiq
5
+ class Configuration
6
+ class << self
7
+
8
+ def configure
9
+ yield self
10
+ end
11
+
12
+ def queue=(value)
13
+ _sidekiq_options['queue'] = _stringify_symbol(value)
14
+ end
15
+
16
+ def backtrace=(value)
17
+ _sidekiq_options['backtrace'] = _stringify_symbol(value)
18
+ end
19
+
20
+ def retry=(value)
21
+ _sidekiq_options['retry'] = _stringify_symbol(value)
22
+ end
23
+
24
+ def pool=(value)
25
+ _sidekiq_options['pool'] = _stringify_symbol(value)
26
+ end
27
+
28
+ def sidekiq_options
29
+ _sidekiq_options.clone
30
+ end
31
+
32
+ private
33
+
34
+ attr_writer :sidekiq_options
35
+
36
+ def _sidekiq_options
37
+ @_sidekiq_options ||= {}
38
+ end
39
+
40
+ def _stringify_symbol(value)
41
+ value.is_a?(Symbol) ? value.to_s : value
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,22 +1,17 @@
1
1
  require 'routemaster/client/connection'
2
2
 
3
3
  module Routemaster
4
- class Client
4
+ module Client
5
5
  module Backends
6
6
  class Sidekiq
7
7
  class Worker
8
8
  include ::Sidekiq::Worker
9
+ extend Forwardable
9
10
 
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
11
+ def_delegator :'Routemaster::Client::Connection', :send_event
12
+ alias :perform :send_event
14
13
 
15
- private
16
-
17
- def _symbolize_keys(hash)
18
- Hash[hash.map{|(k,v)| [k.to_sym,v]}]
19
- end
14
+ private :send_event
20
15
  end
21
16
  end
22
17
  end
@@ -1,22 +1,12 @@
1
1
  require 'routemaster/client/connection'
2
2
 
3
3
  module Routemaster
4
- class Client
4
+ module Client
5
5
  module Backends
6
6
  class Synchronous
7
-
8
7
  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)
8
+ extend Forwardable
9
+ def_delegator :'Routemaster::Client::Connection', :send_event
20
10
  end
21
11
  end
22
12
  end
@@ -0,0 +1,70 @@
1
+ require 'routemaster/client/assertion_helpers'
2
+ require 'routemaster/client/errors'
3
+ require 'routemaster/client/backends/missing_asynchronous'
4
+
5
+ module Routemaster
6
+ module Client
7
+ class Configuration
8
+ class << self
9
+ include AssertionHelpers
10
+
11
+ DEFAULT_TIMEOUT = 5
12
+ DEFAULT_LAZY = false
13
+ DEFAULT_VERIFY_SSL = true
14
+
15
+ attr_accessor :url, :uuid, :timeout, :async_backend, :lazy, :verify_ssl
16
+
17
+ def configure
18
+ yield self
19
+ _validate_all_options!
20
+ end
21
+
22
+ private
23
+
24
+ def _validate_all_options!
25
+ _assert_present! url: url, uuid: uuid
26
+ _assert_valid_url!(url)
27
+ _assert_valid_uuid!(uuid)
28
+ timeout ? _assert_valid_timeout!(timeout) : self.timeout = DEFAULT_TIMEOUT
29
+
30
+ lazy.nil? ? self.lazy = DEFAULT_LAZY : _assert_boolean!(lazy: lazy)
31
+ verify_ssl.nil? ? self.verify_ssl = DEFAULT_VERIFY_SSL : _assert_boolean!(verify_ssl: verify_ssl)
32
+ self.async_backend ||= Backends::MissingAsynchronous
33
+ end
34
+
35
+ def _assert_valid_url!(url)
36
+ assert_valid_url_throwing_error!(url, InvalidAttributeError)
37
+ end
38
+
39
+ def _assert_valid_uuid!(uuid)
40
+ raise MissingAttributeError, "uuid is required" unless uuid
41
+ unless uuid =~ /^[a-z0-9_-]{1,64}$/
42
+ message = "uuid '#{uuid}' is invalid, must only contain alphanumeric characters " +
43
+ "plus _ and - and be 1 to 64 characters"
44
+ raise InvalidAttributeError, message
45
+ end
46
+ end
47
+
48
+ def _assert_valid_timeout!(timeout)
49
+ unless (0..3_600_000).include? timeout
50
+ raise InvalidAttributeError, "timeout '#{timeout}' is invalid, must be an integer between 0 and 3,600,000"
51
+ end
52
+ end
53
+
54
+ def _assert_boolean!(**kwargs)
55
+ kwargs.each do |name, value|
56
+ unless [true, false].include? value
57
+ raise InvalidAttributeError, "#{name} '#{value}' is invalid, must be a boolean value: true or false"
58
+ end
59
+ end
60
+ end
61
+
62
+ def _assert_present!(**kwargs)
63
+ kwargs.each do |name, value|
64
+ raise MissingAttributeError, "#{name} is required" unless value
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,73 +1,72 @@
1
- require 'forwardable'
2
-
3
- # Internal class used by Routemaster::Client
1
+ require 'routemaster/client/configuration'
4
2
 
5
3
  module Routemaster
6
- class Client
7
- class Connection
4
+ module Client
5
+ module Connection
6
+ class << self
7
+ extend Forwardable
8
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
9
+ def post(path, &block)
10
+ http(:post, path, &block)
11
+ end
15
12
 
16
- def http(method, path, &block)
17
- _conn.send(method, path, &block)
18
- end
13
+ def get(path, &block)
14
+ http(:get, path, &block)
15
+ end
19
16
 
20
- def post(path, &block)
21
- http(:post, path, &block)
22
- end
17
+ def delete(path, &block)
18
+ http(:delete, path, &block)
19
+ end
23
20
 
24
- def get(path, &block)
25
- http(:get, path, &block)
26
- end
21
+ def http(method, path, &block)
22
+ _conn.send(method, path, &block)
23
+ end
27
24
 
28
- def delete(path, &block)
29
- http(:delete, path, &block)
30
- end
25
+ def send_event(event, topic, callback, timestamp = nil)
26
+ data = { type: event, url: callback, timestamp: timestamp }
31
27
 
32
- def send_event(event, topic, callback, timestamp = nil)
33
- data = { type: event, url: callback, timestamp: timestamp }
28
+ response = post("/topics/#{topic}") do |r|
29
+ r.headers['Content-Type'] = 'application/json'
30
+ r.body = Oj.dump(_stringify_keys data)
31
+ end
32
+ fail "event rejected (#{response.status})" unless response.success?
34
33
 
35
- response = post("/topics/#{topic}") do |r|
36
- r.headers['Content-Type'] = 'application/json'
37
- r.body = Oj.dump(_stringify_keys data)
34
+ # Any issues would have caused an exception to be thrown
35
+ true
38
36
  end
39
- fail "event rejected (#{response.status})" unless response.success?
40
- end
41
37
 
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
38
+ def subscribe(options)
39
+ response = post('/subscription') do |r|
40
+ r.headers['Content-Type'] = 'application/json'
41
+ r.body = Oj.dump(_stringify_keys options)
42
+ end
47
43
 
48
- unless response.success?
49
- raise 'subscribe rejected'
44
+ unless response.success?
45
+ raise 'subscribe rejected'
46
+ end
50
47
  end
51
- end
52
48
 
53
- private
49
+ private
50
+
51
+ def_delegators :'Routemaster::Client::Configuration', :url, :timeout, :uuid, :verify_ssl
54
52
 
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)
53
+ def _stringify_keys(hash)
54
+ hash.dup.tap do |h|
55
+ h.keys.each do |k|
56
+ h[k.to_s] = h.delete(k)
57
+ end
59
58
  end
60
59
  end
61
- end
62
60
 
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
61
+ def _conn
62
+ @_conn ||= Faraday.new(url, ssl: { verify: verify_ssl }) do |f|
63
+ f.request :retry, max: 2, interval: 100e-3, backoff_factor: 2
64
+ f.request :basic_auth, uuid, 'x'
65
+ f.adapter :typhoeus
68
66
 
69
- f.options.timeout = @_timeout
70
- f.options.open_timeout = @_timeout
67
+ f.options.timeout = timeout
68
+ f.options.open_timeout = timeout
69
+ end
71
70
  end
72
71
  end
73
72
  end
@@ -0,0 +1,12 @@
1
+ module Routemaster
2
+ module Client
3
+ class Error < StandardError; end
4
+ class ArgumentError < ArgumentError; end
5
+
6
+ class InvalidArgumentError < ArgumentError; end
7
+ class ConfigurationError < Error; end
8
+ class MissingAsyncBackendError < Error; end
9
+ class MissingAttributeError < ConfigurationError; end
10
+ class InvalidAttributeError < ConfigurationError; end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  module Routemaster
2
- class Client
3
- VERSION = '2.1.0'
2
+ module Client
3
+ VERSION = '3.0.0'
4
4
  end
5
5
  end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'spec/support/configuration_helper'
3
+ require 'routemaster/client/backends/sidekiq/configuration'
4
+
5
+ describe Routemaster::Client::Backends::Sidekiq::Configuration do
6
+ reset_sidekiq_config_between_tests!
7
+
8
+ describe '#configure' do
9
+
10
+ it 'sets sidekiq_options to empty hash if nothing is specified' do
11
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure {}
12
+ expect(sidekiq_options).to eq({})
13
+ end
14
+
15
+ describe 'queue' do
16
+ it 'sets the queue with a string key, and stringifies the value' do
17
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure do |c|
18
+ c.queue = :nice_queue
19
+ end
20
+ expect(sidekiq_options['queue']).to eq 'nice_queue'
21
+ end
22
+ end
23
+
24
+ describe 'backtrace' do
25
+ it 'sets the backtrace with a string key' do
26
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure do |c|
27
+ c.backtrace = true
28
+ end
29
+ expect(sidekiq_options['backtrace']).to eq true
30
+ end
31
+ end
32
+
33
+ describe 'retry' do
34
+ it 'sets the retry with a string key' do
35
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure do |c|
36
+ c.retry = true
37
+ end
38
+ expect(sidekiq_options['retry']).to eq true
39
+ end
40
+ end
41
+
42
+ describe 'pool' do
43
+ it 'sets the pool with a string key' do
44
+ pool = double
45
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure do |c|
46
+ c.pool = pool
47
+ end
48
+ expect(sidekiq_options['pool']).to eq pool
49
+ end
50
+ end
51
+
52
+ describe 'sidekiq_options' do
53
+ it 'cannot be modified after configuration' do
54
+ Routemaster::Client::Backends::Sidekiq::Configuration.configure {}
55
+ Routemaster::Client::Backends::Sidekiq::Configuration.sidekiq_options[:key_that_dont_exist] = 'Weeee'
56
+ expect(Routemaster::Client::Backends::Sidekiq::Configuration.sidekiq_options).to_not have_key(:key_that_dont_exist)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def sidekiq_options
63
+ Routemaster::Client::Backends::Sidekiq::Configuration.sidekiq_options
64
+ end
@@ -0,0 +1,215 @@
1
+ require 'spec_helper'
2
+ require 'spec/support/configuration_helper'
3
+ require 'routemaster/client/configuration'
4
+
5
+ describe Routemaster::Client::Configuration do
6
+
7
+ reset_config_between_tests!
8
+
9
+ describe '#configure' do
10
+ describe 'url' do
11
+ it 'raises an error if url is not defined' do
12
+ configure = -> do
13
+ Routemaster::Client::Configuration.configure do |config|
14
+ config.uuid = 'I_am_a_magical_uuid'
15
+ end
16
+ end
17
+
18
+ expected_message = 'url is required'
19
+ expect(&configure).to raise_error Routemaster::Client::MissingAttributeError, expected_message
20
+ end
21
+
22
+ it 'raises an error if url is invalid' do
23
+ configure = -> do
24
+ Routemaster::Client::Configuration.configure do |config|
25
+ config.uuid = 'I_am_a_magical_uuid'
26
+ config.url = 'not a proper url at all'
27
+ end
28
+ end
29
+
30
+ expected_message = "url 'not a proper url at all' is invalid, must be an https url"
31
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
32
+ end
33
+
34
+ it 'raises an error if url is not https' do
35
+ configure = -> do
36
+ Routemaster::Client::Configuration.configure do |config|
37
+ config.uuid = 'I_am_a_magical_uuid'
38
+ config.url = 'http://example.com'
39
+ end
40
+ end
41
+
42
+ expected_message = "url 'http://example.com' is invalid, must be an https url"
43
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
44
+ end
45
+ end
46
+
47
+ describe 'uuid' do
48
+ it 'raises an error if uuid if not defined' do
49
+ configure = -> do
50
+ Routemaster::Client::Configuration.configure do |config|
51
+ config.url = 'https://example.com'
52
+ end
53
+ end
54
+
55
+ expect(&configure).to raise_error Routemaster::Client::MissingAttributeError
56
+ end
57
+
58
+ it 'requires an error if uuid is not alphanumeric (plus dashes and underscores)' do
59
+ configure = -> do
60
+ Routemaster::Client::Configuration.configure do |config|
61
+ config.url = 'https://example.com'
62
+ config.uuid = '$$$INVALID'
63
+ end
64
+ end
65
+
66
+ expected_message = "uuid '$$$INVALID' is invalid, must only contain alphanumeric characters " +
67
+ 'plus _ and - and be 1 to 64 characters'
68
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
69
+ end
70
+ end
71
+
72
+ it "does not raise an error if both uuid and url are defined and are valid" do
73
+ configure = -> do
74
+ Routemaster::Client::Configuration.configure do |config|
75
+ config.url = 'https://lovely.com'
76
+ config.uuid = 'uiiiiiiidddd'
77
+ end
78
+ end
79
+
80
+ expect(&configure).to_not raise_error
81
+ end
82
+ end
83
+
84
+ describe 'timeout' do
85
+ it 'raises an error if timeout is not an integer' do
86
+ configure = -> do
87
+ Routemaster::Client::Configuration.configure do |config|
88
+ config.url = 'https://example.com'
89
+ config.uuid = "uiiiiiiidddd"
90
+ config.timeout = 'one'
91
+ end
92
+ end
93
+
94
+ expected_message = "timeout 'one' is invalid, must be an integer between 0 and 3,600,000"
95
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
96
+ end
97
+
98
+ it 'raises an error if timeout is too large' do
99
+ configure = -> do
100
+ Routemaster::Client::Configuration.configure do |config|
101
+ config.url = 'https://example.com'
102
+ config.uuid = 'uiiiiiiidddd'
103
+ config.timeout = 1_000_000_000_000_000_000_000_000_000_000_000_000
104
+ end
105
+ end
106
+
107
+ expected_message = "timeout '1000000000000000000000000000000000000' is invalid, must be an integer between 0 and 3,600,000"
108
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
109
+ end
110
+
111
+ it 'does not raise an error if the timeout is valid' do
112
+ configure = -> do
113
+ Routemaster::Client::Configuration.configure do |config|
114
+ config.url = 'https://lovely.com'
115
+ config.uuid = 'uiiiiiiidddd'
116
+ config.timeout = 2
117
+ end
118
+ end
119
+
120
+ expect(&configure).to_not raise_error
121
+ end
122
+
123
+ it 'will default to 5 if not supplied' do
124
+ configure = -> do
125
+ Routemaster::Client::Configuration.configure do |config|
126
+ config.url = 'https://lovely.com'
127
+ config.uuid = 'uiiiiiiidddd'
128
+ end
129
+ end
130
+
131
+ expect(&configure).to_not raise_error
132
+ expect(Routemaster::Client::Configuration.timeout).to eq 5
133
+ end
134
+ end
135
+
136
+ describe 'lazy' do
137
+ it 'raises an error if lazy is not a boolean' do
138
+ configure = -> do
139
+ Routemaster::Client::Configuration.configure do |config|
140
+ config.url = 'https://example.com'
141
+ config.uuid = 'uiiiiiiidddd'
142
+ config.lazy = 'yes'
143
+ end
144
+ end
145
+
146
+ expected_message = "lazy 'yes' is invalid, must be a boolean value: true or false"
147
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
148
+ end
149
+
150
+ it 'does not raise an error if lazy is a boolean' do
151
+ configure = -> do
152
+ Routemaster::Client::Configuration.configure do |config|
153
+ config.url = 'https://example.com'
154
+ config.uuid = 'uiiiiiiidddd'
155
+ config.lazy = true
156
+ end
157
+ end
158
+
159
+ expect(&configure).to_not raise_error
160
+ expect(Routemaster::Client::Configuration.lazy).to eq true
161
+ end
162
+
163
+ it 'will default to false if not supplied' do
164
+ configure = -> do
165
+ Routemaster::Client::Configuration.configure do |config|
166
+ config.url = 'https://lovely.com'
167
+ config.uuid = 'uiiiiiiidddd'
168
+ end
169
+ end
170
+
171
+ expect(&configure).to_not raise_error
172
+ expect(Routemaster::Client::Configuration.lazy).to eq false
173
+ end
174
+ end
175
+
176
+ describe 'verify_ssl' do
177
+ it 'raises an error if verify_ssl is not a boolean' do
178
+ configure = -> do
179
+ Routemaster::Client::Configuration.configure do |config|
180
+ config.url = 'https://example.com'
181
+ config.uuid = 'uiiiiiiidddd'
182
+ config.verify_ssl = 'yes'
183
+ end
184
+ end
185
+
186
+ expected_message = "verify_ssl 'yes' is invalid, must be a boolean value: true or false"
187
+ expect(&configure).to raise_error Routemaster::Client::InvalidAttributeError, expected_message
188
+ end
189
+
190
+ it 'does not raise an error if verify_ssl is a boolean' do
191
+ configure = -> do
192
+ Routemaster::Client::Configuration.configure do |config|
193
+ config.url = 'https://example.com'
194
+ config.uuid = 'uiiiiiiidddd'
195
+ config.verify_ssl = false
196
+ end
197
+ end
198
+
199
+ expect(&configure).to_not raise_error
200
+ expect(Routemaster::Client::Configuration.verify_ssl).to eq false
201
+ end
202
+
203
+ it 'will default to true if not supplied' do
204
+ configure = -> do
205
+ Routemaster::Client::Configuration.configure do |config|
206
+ config.url = 'https://lovely.com'
207
+ config.uuid = 'uiiiiiiidddd'
208
+ end
209
+ end
210
+
211
+ expect(&configure).to_not raise_error
212
+ expect(Routemaster::Client::Configuration.verify_ssl).to eq true
213
+ end
214
+ end
215
+ end