sitehub 0.5.0.alpha7 → 0.5.0.alpha8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da2a5c038108c8668145aa88a4c0a828b6b922a9
4
- data.tar.gz: 54aa60369801ce0c17e5cf63534476b89982ed7b
3
+ metadata.gz: 2389132d290dc9bcff1fca5999cc75802e31af1a
4
+ data.tar.gz: dfca66250eac7e57b5ec53002d2c662ede650688
5
5
  SHA512:
6
- metadata.gz: 41ecea0098d4388ca85887cf8c5bdbab22f29d10f05c16559378aa89a893fcc6149e66eb538ee8b54ce0635ca2e168f4e23b03004e94d5f0765b5a991028fdd1
7
- data.tar.gz: 1e0da0d04f15a22558b26140884f4f5f7ba20f13515d35d8ee7b67bf60836fd6698ec97110ef53c683043761a336f12ca19824ecafd5ef8cfd0eec6396596d87
6
+ metadata.gz: 27a39c2ac4aea35dcf4ae1b73a7ad8fd77c23f3f0fc1e22c4829a0052eec38cec2d1d4eaac32e84906e442297855eefa7afce5e6eccf1a33605a04ca84fcd001
7
+ data.tar.gz: fb3e3f8a9d9eb65ef2ad3b714bec96a716cbaf5d8b0fde70dd5e5418a887901e7f07403f4e63249e4f736ce4e10a7f723afe90d21b2312803e0c22de771c4341
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sitehub (0.5.0.alpha7)
4
+ sitehub (0.5.0.alpha8)
5
5
  activesupport
6
6
  em-http-request
7
7
  em-synchrony
@@ -7,8 +7,15 @@ class SiteHub
7
7
  include Middleware
8
8
  extend GetterSetterMethods
9
9
 
10
- attr_reader :core
11
- getter_setters :access_logger, :error_logger, :config_server
10
+ DEFAULT_CACHING_OPTIONS = { expires_in: 30 }.freeze
11
+
12
+ attr_reader :core, :config_server_url, :config_server_caching_options
13
+ getter_setters :access_logger, :error_logger
14
+
15
+ def config_server(url, caching_options: DEFAULT_CACHING_OPTIONS)
16
+ @config_server_url = url
17
+ @config_server_caching_options = caching_options
18
+ end
12
19
 
13
20
  def force_ssl(except: [])
14
21
  @force_ssl = true
@@ -22,7 +29,7 @@ class SiteHub
22
29
 
23
30
  def build
24
31
  add_default_middleware
25
- use ConfigLoader, config_server if config_server
32
+ use ConfigLoader, config_server_url, caching_options: config_server_caching_options if config_server_url
26
33
  apply_middleware(core.build)
27
34
  end
28
35
 
@@ -15,7 +15,7 @@ require_relative 'downstream_client'
15
15
 
16
16
  class SiteHub
17
17
  class CandidateRoutes
18
- class InvalidDefinitionException < Exception
18
+ class InvalidDefinitionError < StandardError
19
19
  end
20
20
 
21
21
  ROUTES_WITH_SPLITS_MSG = 'you cant register routes and splits at the same level'.freeze
@@ -37,11 +37,11 @@ class SiteHub
37
37
  child_label = id.child_label(label)
38
38
 
39
39
  route = if block
40
- raise InvalidDefinitionException, candidate_definition_msg unless percentage || rule
40
+ raise InvalidDefinitionError, candidate_definition_msg unless percentage || rule
41
41
  warn(IGNORING_URL_MSG) if url
42
42
  new(rule: rule, id: child_label, &block).build
43
43
  else
44
- raise InvalidDefinitionException, URL_REQUIRED_MSG unless url
44
+ raise InvalidDefinitionError, URL_REQUIRED_MSG unless url
45
45
  forward_proxy(url: url, label: child_label, rule: rule)
46
46
  end
47
47
 
@@ -56,7 +56,7 @@ class SiteHub
56
56
  def candidates(collection = nil)
57
57
  return @endpoints ||= Collection::RouteCollection.new unless collection
58
58
 
59
- raise InvalidDefinitionException, ROUTES_WITH_SPLITS_MSG if @endpoints && !@endpoints.equal?(collection)
59
+ raise InvalidDefinitionError, ROUTES_WITH_SPLITS_MSG if @endpoints && !@endpoints.equal?(collection)
60
60
  @endpoints = collection
61
61
  end
62
62
 
@@ -96,7 +96,7 @@ class SiteHub
96
96
  return unless block_given?
97
97
 
98
98
  instance_eval(&block)
99
- raise InvalidDefinitionException unless valid?
99
+ raise InvalidDefinitionError unless valid?
100
100
  end
101
101
 
102
102
  def method_missing(method, *args, &block)
@@ -1,6 +1,6 @@
1
1
  class SiteHub
2
2
  class Collection < Hash
3
- class DuplicateVersionException < Exception
3
+ class DuplicateVersionError < StandardError
4
4
  end
5
5
 
6
6
  module ClassMethods
@@ -16,7 +16,7 @@ class SiteHub
16
16
  alias_method :add_backup, :add
17
17
 
18
18
  send(:define_method, :add) do |id, value, *args|
19
- raise DuplicateVersionException, UNIQUE_LABELS_MSG if key?(id)
19
+ raise DuplicateVersionError, UNIQUE_LABELS_MSG if key?(id)
20
20
  add_backup id, value, *args
21
21
  end
22
22
  end
@@ -1,4 +1,7 @@
1
1
  require_relative '../collection'
2
+ # rubocop:disable Lint/UnusedMethodArgument -
3
+ # this is because #resolve is supporting a duck typed interface and needs the env parameter
4
+
2
5
  class SiteHub
3
6
  class Collection < Hash
4
7
  class RouteCollection < Collection
@@ -3,7 +3,7 @@ require_relative 'split_route_collection/split'
3
3
  class SiteHub
4
4
  class Collection < Hash
5
5
  class SplitRouteCollection < Collection
6
- class InvalidSplitException < Exception
6
+ class InvalidSplitException < StandardError
7
7
  end
8
8
 
9
9
  FIXNUM_ERR_MSG = 'splits must be a Fixnum'.freeze
@@ -0,0 +1,32 @@
1
+ class SiteHub
2
+ class ConfigServer
3
+ BAD_JSON_MSG = 'Illegal JSON returned from config server: %s'.freeze
4
+ UNABLE_TO_CONTACT_SERVER_MSG = 'Unabled to contact server: %s'.freeze
5
+ NON_200_RESPONSE_MSG = 'Config server did not respond with a 200, got %s'.freeze
6
+
7
+ class Error < StandardError
8
+ end
9
+
10
+ attr_reader :server_url, :http_client
11
+ def initialize(url)
12
+ @server_url = url
13
+ @http_client = Faraday.new(ssl: { verify: false }) do |con|
14
+ con.adapter :em_synchrony
15
+ end
16
+ end
17
+
18
+ def get
19
+ response = http_client.get(server_url)
20
+ raise Error, NON_200_RESPONSE_MSG % response.status unless response.status == 200
21
+ parse_response(response.body)
22
+ rescue Faraday::Error => e
23
+ raise Error, UNABLE_TO_CONTACT_SERVER_MSG % e.message
24
+ end
25
+
26
+ def parse_response(response_body)
27
+ JSON(response_body, symbolize_names: true)
28
+ rescue JSON::ParserError
29
+ raise Error, BAD_JSON_MSG % response_body
30
+ end
31
+ end
32
+ end
@@ -3,10 +3,10 @@ require 'sitehub/candidate_routes'
3
3
  require 'forwardable'
4
4
 
5
5
  class SiteHub
6
- class InvalidProxyDefinitionException < Exception
6
+ class InvalidProxyDefinitionException < StandardError
7
7
  end
8
8
 
9
- class ConfigError < Exception
9
+ class ConfigError < StandardError
10
10
  end
11
11
 
12
12
  class Core
@@ -9,6 +9,10 @@ require 'em-http'
9
9
  class SiteHub
10
10
  module Middleware
11
11
  class CandidateRouteMappings < Hash
12
+ INVALID_PATH_MATCHER = 'Matcher for path (%s) was not a valid regexp: %s'.freeze
13
+ class InvalidPathMatcherError < StandardError
14
+ end
15
+
12
16
  NIL_ROUTE = NilRoute.new
13
17
 
14
18
  include Equality
@@ -39,6 +43,8 @@ class SiteHub
39
43
  return
40
44
  end
41
45
 
46
+ mapped_path = string_to_regexp(mapped_path) if string_containing_regexp?(mapped_path)
47
+
42
48
  self[mapped_path] = CandidateRoutes.new(sitehub_cookie_name: sitehub_cookie_name,
43
49
  mapped_path: mapped_path,
44
50
  &block).tap do |builder|
@@ -60,6 +66,20 @@ class SiteHub
60
66
  end
61
67
  end
62
68
  end
69
+
70
+ def string_containing_regexp?(obj)
71
+ return false unless obj.is_a?(String)
72
+ obj.start_with?('%r{') && obj.end_with?('}')
73
+ end
74
+
75
+ private
76
+
77
+ def string_to_regexp(mapped_path)
78
+ regexp_string = mapped_path.to_s.sub(/^%r{/, '').sub(/}$/, '')
79
+ Regexp.compile(regexp_string)
80
+ rescue RegexpError => e
81
+ raise InvalidPathMatcherError, format(INVALID_PATH_MATCHER, regexp_string, e.message)
82
+ end
63
83
  end
64
84
  end
65
85
  end
@@ -1,36 +1,33 @@
1
1
  require 'active_support'
2
+ require 'sitehub/config_server'
2
3
  class SiteHub
3
- class ConfigServer
4
- attr_reader :server_url, :http_client
5
- def initialize(url)
6
- @server_url = url
7
- @http_client = Faraday.new(ssl: { verify: false }) do |con|
8
- con.adapter :em_synchrony
9
- end
10
- end
11
-
12
- def get
13
- JSON(http_client.get(server_url).body, symbolize_names: true)
14
- end
15
- end
16
-
17
4
  module Middleware
18
5
  class ConfigLoader
19
- attr_reader :config_server, :app, :cache
6
+ attr_reader :config_server, :app, :cache, :caching_options
20
7
 
21
- def initialize(_app, config_server_url)
8
+ def initialize(_app, config_server_url, caching_options:)
22
9
  @config_server = ConfigServer.new(config_server_url)
23
10
  @cache = ActiveSupport::Cache::MemoryStore.new(size: 1.megabytes)
11
+ @caching_options = caching_options
24
12
  end
25
13
 
26
14
  def call(env)
27
- load_config
15
+ begin
16
+ load_config
17
+ rescue ConfigServer::Error => e
18
+ if @app
19
+ env[ERRORS] << e.message
20
+ else
21
+ raise e unless @app
22
+ end
23
+ end
24
+
28
25
  @app.call env
29
26
  end
30
27
 
31
28
  # TODO: handle errors connecting to the config server
32
29
  def load_config
33
- config = cache.fetch(:sitehub_config, expires_in: 30) do
30
+ config = cache.fetch(:sitehub_config, caching_options) do
34
31
  config_server.get
35
32
  end
36
33
 
@@ -1,3 +1,3 @@
1
1
  class SiteHub
2
- VERSION = '0.5.0.alpha7'.freeze
2
+ VERSION = '0.5.0.alpha8'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'cgi'
2
+
2
3
  describe 'access_logs' do
3
4
  let(:downstream_url) { 'http://localhost:12345/experiment1' }
4
5
 
@@ -30,10 +31,13 @@ describe 'access_logs' do
30
31
  let(:query_string) { '' }
31
32
  let(:request_url) { "/endpoint#{query_string.empty? ? '' : "?#{query_string}"}" }
32
33
 
33
- subject do
34
+ before do
34
35
  query_string_hash = CGI.parse(query_string).collect { |key, value| [key, value.first] }.to_h
35
36
  stub_request(:get, downstream_url).with(query: query_string_hash)
36
37
  get(request_url)
38
+ end
39
+
40
+ subject do
37
41
  access_logger.string
38
42
  end
39
43
 
@@ -41,20 +45,28 @@ describe 'access_logs' do
41
45
  context 'present' do
42
46
  let(:query_string) { 'key=value' }
43
47
  it 'logs it' do
44
- expect(subject).to include(query_string)
48
+ expect(subject).to include("GET #{request_url} => #{downstream_url}")
45
49
  end
46
50
  end
47
51
 
48
52
  context 'not present' do
49
53
  let(:query_string) { '' }
50
54
  it 'is not logged' do
55
+ expect(subject).to match(/"GET\s#{request_url}\s=>\s#{downstream_url}\s/)
51
56
  expect(subject).to include(query_string)
52
57
  end
53
58
  end
54
59
  end
55
60
 
61
+ module WebMock
62
+ def self.last_request
63
+ WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first
64
+ end
65
+ end
66
+
56
67
  it 'logs the transaction id' do
57
- expect(subject).to match(/transaction_id:.*?\s/)
68
+ expected_id = WebMock.last_request.headers['Sitehub-Transaction-Id']
69
+ expect(subject).to match(/transaction_id:#{expected_id}/)
58
70
  end
59
71
 
60
72
  it 'logs the response status' do
@@ -69,7 +81,6 @@ describe 'access_logs' do
69
81
  processing_time_matcher = '\d{1}\.\d{4}'
70
82
  transaction_id_matcher = '[a-z\d]+'
71
83
  expected_response_status = 200
72
- puts subject
73
84
  expect(subject).to match(/transaction_id:#{transaction_id_matcher}:\s"GET\s#{request_url}\s=>\s#{downstream_url}\s"\s#{expected_response_status}\s-\s#{processing_time_matcher}/)
74
85
  end
75
86
  end
@@ -0,0 +1,77 @@
1
+ require 'stringio'
2
+ describe 'error handling' do
3
+ context 'using config server' do
4
+ CONFIG_SERVER_URL = 'http://the.config.server'.freeze
5
+ DOWNSTREAM_URL = 'http://downstream.url'.freeze
6
+ ERROR_LOGGER = StringIO.new
7
+ ACCESS_LOGGER = StringIO.new
8
+
9
+ include_context :sitehub_json
10
+
11
+ let(:config) do
12
+ { proxies: [
13
+ {
14
+ path: '/',
15
+ default: DOWNSTREAM_URL
16
+ }
17
+ ] }
18
+ end
19
+
20
+ let(:downstream_response) { 'downstream response' }
21
+
22
+ before do
23
+ stub_request(:get, DOWNSTREAM_URL).and_return(body: downstream_response)
24
+ end
25
+ let(:app) do
26
+ sitehub = SiteHub.build do
27
+ config_server(CONFIG_SERVER_URL, caching_options: { force: true })
28
+ error_logger ERROR_LOGGER
29
+ access_logger ACCESS_LOGGER
30
+ end
31
+ Async::Middleware.new(sitehub)
32
+ end
33
+
34
+ it 'reads the config for routing' do
35
+ stub_request(:get, CONFIG_SERVER_URL).and_return(body: config.to_json)
36
+ get('/')
37
+ expect(app.last_response.body).to eq([downstream_response])
38
+ end
39
+
40
+ context 'config server is broken' do
41
+ let(:bad_status) { 500 }
42
+ context 'config has not been loaded' do
43
+ it 'returns an error' do
44
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: 500)
45
+ get('/')
46
+ expect(app.last_response.status).to eq(500)
47
+ end
48
+
49
+ it 'logs an error' do
50
+ bad_status = 500
51
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: bad_status)
52
+ get('/')
53
+ expect(ERROR_LOGGER.string).to include(SiteHub::ConfigServer::NON_200_RESPONSE_MSG % bad_status)
54
+ end
55
+ end
56
+
57
+ context 'config has been correctly loaded once' do
58
+ before do
59
+ stub_request(:get, CONFIG_SERVER_URL).and_return(body: config.to_json)
60
+ get('/')
61
+
62
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: bad_status)
63
+ end
64
+
65
+ it 'uses the old config' do
66
+ get('/')
67
+ expect(app.last_response.body).to eq([downstream_response])
68
+ end
69
+
70
+ it 'logs the failure to read config' do
71
+ get('/')
72
+ expect(ERROR_LOGGER.string).to include(SiteHub::ConfigServer::NON_200_RESPONSE_MSG % bad_status)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -163,6 +163,27 @@ class SiteHub
163
163
 
164
164
  expect(middleware_stack).to eq(expected_middleware)
165
165
  end
166
+
167
+ it 'defaults caching policy' do
168
+ subject.config_server(:server_url)
169
+
170
+ sitehub = subject.build
171
+
172
+ config_loader = find_middleware(sitehub, Middleware::ConfigLoader)
173
+ expect(config_loader.caching_options).to eq(described_class::DEFAULT_CACHING_OPTIONS)
174
+ end
175
+
176
+ context 'caching options specified' do
177
+ it 'uses them' do
178
+ caching_options = { force: true }
179
+ subject.config_server(:server_url, caching_options: caching_options)
180
+
181
+ sitehub = subject.build
182
+
183
+ config_loader = find_middleware(sitehub, Middleware::ConfigLoader)
184
+ expect(config_loader.caching_options).to eq(caching_options)
185
+ end
186
+ end
166
187
  end
167
188
 
168
189
  it 'adds them in the right order' do
@@ -148,7 +148,7 @@ class SiteHub
148
148
  it 'raises an error' do
149
149
  subject.candidates(Collection::SplitRouteCollection.new)
150
150
  expect { subject.candidates(Collection::RouteCollection.new) }
151
- .to raise_error(CandidateRoutes::InvalidDefinitionException)
151
+ .to raise_error(CandidateRoutes::InvalidDefinitionError)
152
152
  end
153
153
  end
154
154
  end
@@ -301,7 +301,7 @@ class SiteHub
301
301
  subject.candidates(Collection::SplitRouteCollection.new)
302
302
  expected_message = described_class::PERCENTAGE_NOT_SPECIFIED_MSG
303
303
  expect { subject.add(label: :label) {} }
304
- .to raise_exception described_class::InvalidDefinitionException, expected_message
304
+ .to raise_exception described_class::InvalidDefinitionError, expected_message
305
305
  end
306
306
  end
307
307
 
@@ -310,7 +310,7 @@ class SiteHub
310
310
  subject.candidates(Collection::RouteCollection.new)
311
311
  expected_message = described_class::RULE_NOT_SPECIFIED_MSG
312
312
  expect { subject.add(label: :label) {} }
313
- .to raise_exception described_class::InvalidDefinitionException, expected_message
313
+ .to raise_exception described_class::InvalidDefinitionError, expected_message
314
314
  end
315
315
  end
316
316
  end
@@ -327,8 +327,11 @@ class SiteHub
327
327
  rule = proc { true }
328
328
  subject.add(rule: rule, label: :label, &block)
329
329
 
330
- expected_endpoints = described_class.new(id: :label, sitehub_cookie_name: :cookie_name,
331
- rule: rule, mapped_path: subject.mapped_path, &block).tap do |builder|
330
+ expected_endpoints = described_class.new(id: :label,
331
+ sitehub_cookie_name: :cookie_name,
332
+ rule: rule,
333
+ mapped_path: subject.mapped_path,
334
+ &block).tap do |builder|
332
335
  builder.sitehub_cookie_name subject.sitehub_cookie_name
333
336
  end.build
334
337
 
@@ -342,7 +345,7 @@ class SiteHub
342
345
  subject.add rule: rule, label: :label do
343
346
  split percentage: 20, url: :url, label: :label1
344
347
  end
345
- end.to raise_exception described_class::InvalidDefinitionException
348
+ end.to raise_exception described_class::InvalidDefinitionError
346
349
  end
347
350
  end
348
351
  end
@@ -464,7 +467,7 @@ class SiteHub
464
467
  context 'already set with a different collection' do
465
468
  it 'raise an error' do
466
469
  subject.candidates(:collection1)
467
- expect { subject.candidates(:collection2) }.to raise_exception described_class::InvalidDefinitionException
470
+ expect { subject.candidates(:collection2) }.to raise_exception described_class::InvalidDefinitionError
468
471
  end
469
472
  end
470
473
  end
@@ -32,7 +32,7 @@ class SiteHub
32
32
  subject.add(duplicate.id, duplicate)
33
33
  expected_message = described_class::ClassMethods::UNIQUE_LABELS_MSG
34
34
  expect { subject.add(duplicate.id, duplicate) }
35
- .to raise_exception described_class::DuplicateVersionException, expected_message
35
+ .to raise_exception described_class::DuplicateVersionError, expected_message
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,48 @@
1
+ require 'sitehub/config_server'
2
+
3
+ class SiteHub
4
+ describe ConfigServer do
5
+ include_context :sitehub_json
6
+
7
+ let(:server_url) { 'http://www.server.url' }
8
+ let(:config) { { config: 'value' } }
9
+
10
+ subject do
11
+ described_class.new(server_url)
12
+ end
13
+
14
+ describe '#get' do
15
+ context 'non 200 returned' do
16
+ it 'raises an error' do
17
+ bad_response_code = 500
18
+ stub_request(:get, server_url).to_return(body: config.to_json, status: bad_response_code)
19
+ expected_message = described_class::NON_200_RESPONSE_MSG % bad_response_code
20
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
21
+ end
22
+ end
23
+
24
+ context 'exception thrown in client' do
25
+ it 'raises an error' do
26
+ error_msg = 'error from library'
27
+ expect_any_instance_of(Faraday::Connection).to receive(:get).and_raise(Faraday::ConnectionFailed, error_msg)
28
+ expected_message = described_class::UNABLE_TO_CONTACT_SERVER_MSG % error_msg
29
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
30
+ end
31
+ end
32
+
33
+ context 'malformed json returned' do
34
+ it 'returns an error' do
35
+ bad_json = 'bad json'
36
+ stub_request(:get, server_url).to_return(body: bad_json)
37
+ expected_message = described_class::BAD_JSON_MSG % bad_json
38
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
39
+ end
40
+ end
41
+
42
+ it 'returns config as a hash' do
43
+ stub_request(:get, server_url).to_return(body: config.to_json)
44
+ expect(subject.get).to eq(config)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -45,6 +45,15 @@ class SiteHub
45
45
  end
46
46
  end
47
47
 
48
+ context 'query string' do
49
+ let(:params) { { 'key' => 'value' } }
50
+ let(:env) { env_for(path: mapped_path, params_or_body: params) }
51
+ it 'preserves the query string when forwarding the request' do
52
+ stub_request(http_method, current_version_url).with(query: params)
53
+ subject.call(request)
54
+ end
55
+ end
56
+
48
57
  it 'preserves the headers when forwarding request' do
49
58
  http_headers['HTTP_HEADER'] = 'value'
50
59
  subject.call(request)
@@ -21,10 +21,10 @@ class SiteHub
21
21
  end
22
22
 
23
23
  before do
24
- subject.init
24
+ # subject.init
25
25
  end
26
26
 
27
- describe '#add_proxy' do
27
+ describe '#add_route' do
28
28
  def route(app, id:)
29
29
  Route.new(app,
30
30
  id: id,
@@ -41,6 +41,51 @@ class SiteHub
41
41
  end
42
42
  end
43
43
 
44
+ context 'mapped_path' do
45
+ let(:expected_route) do
46
+ proxy = ForwardProxy.new(mapped_path: expected_mapping, mapped_url: base_url)
47
+ route(proxy, id: :current)
48
+ end
49
+
50
+ context 'mapped_path is a string' do
51
+ let(:mapped_path) { 'string' }
52
+ let(:expected_mapping) { mapped_path }
53
+ it 'stores the route against that strubg' do
54
+ expect(subject[mapped_path][:current]).to eq(expected_route)
55
+ end
56
+ end
57
+
58
+ context 'mapped_path is a valid regex' do
59
+ let(:mapped_path) { /regular_expression/ }
60
+ let(:expected_mapping) { mapped_path }
61
+ it 'stores the route against that regexp' do
62
+ expect(subject[mapped_path][:current]).to eq(expected_route)
63
+ end
64
+ end
65
+
66
+ context 'is a string containing a valid regexp' do
67
+ let(:expected_mapping) { /regexp/ }
68
+ let(:mapped_path) { '%r{regexp}' }
69
+ it 'stores the route against the string coverted to a regexp' do
70
+ expect(subject[expected_mapping][:current]).to eq(expected_route)
71
+ end
72
+ end
73
+
74
+ context 'is a string containing malformed regexp' do
75
+ let(:mapped_path) { '%r{*}' }
76
+
77
+ it 'raises an error' do
78
+ expected_message = begin
79
+ Regexp.compile('*')
80
+ rescue RegexpError => e
81
+ format(described_class::INVALID_PATH_MATCHER, '*', e.message)
82
+ end
83
+
84
+ expect { subject }.to raise_error(described_class::InvalidPathMatcherError, expected_message)
85
+ end
86
+ end
87
+ end
88
+
44
89
  context 'url specified' do
45
90
  let(:expected_route) do
46
91
  proxy = ForwardProxy.new(mapped_path: mapped_path, mapped_url: :url)
@@ -145,6 +190,28 @@ class SiteHub
145
190
  end
146
191
  end
147
192
  end
193
+
194
+ describe '#string_containing_regexp?' do
195
+ context 'parameter is not a string' do
196
+ it 'it returns false' do
197
+ expect(subject.string_containing_regexp?(//)).to eq(false)
198
+ end
199
+ end
200
+
201
+ context 'parameter is a string' do
202
+ context 'contains a regexp' do
203
+ it 'it returns true' do
204
+ expect(subject.string_containing_regexp?('%r{}')).to be(true)
205
+ end
206
+ end
207
+
208
+ context 'does not contain a regexp' do
209
+ it 'returns false' do
210
+ expect(subject.string_containing_regexp?('')).to eq(false)
211
+ end
212
+ end
213
+ end
214
+ end
148
215
  end
149
216
  end
150
217
  end
@@ -26,24 +26,40 @@ class SiteHub
26
26
  stub_request(:get, server_url).to_return(body: config.to_json)
27
27
  end
28
28
 
29
+ let(:no_caching) { { force: true } }
29
30
  subject do
30
- described_class.new(:app, server_url)
31
+ described_class.new(:app, server_url, caching_options: no_caching)
31
32
  end
32
33
 
33
34
  describe '#load_config' do
34
- it 'loads config' do
35
- expect(subject.app).to be_nil
36
- subject.load_config
35
+ subject do
36
+ caching_enabled = { expires_in: 30 }
37
+ described_class.new(:app, server_url, caching_options: caching_enabled)
38
+ end
37
39
 
38
- expected_core = Core.new do
40
+ let(:expected_core) do
41
+ Core.new do
39
42
  sitehub_cookie_name 'sitehub.recorded_route'
40
43
  proxy '/route_1' do
41
44
  route label: :label_1, url: 'http://lvl-up.uk/'
42
45
  end
43
46
  end.build
47
+ end
48
+
49
+ it 'loads config' do
50
+ expect(subject.app).to be_nil
51
+ subject.load_config
44
52
 
45
53
  expect(subject.app).to eq(expected_core)
46
54
  end
55
+
56
+ it 'loads it from cache' do
57
+ different_config = { proxies: [] }
58
+ subject.load_config
59
+ stub_request(:get, server_url).to_return(body: different_config.to_json)
60
+ subject.load_config
61
+ expect(subject.app).to eq(expected_core)
62
+ end
47
63
  end
48
64
 
49
65
  describe '#call' do
@@ -58,6 +74,61 @@ class SiteHub
58
74
 
59
75
  expect(subject.call(:env)).to eq(response)
60
76
  end
77
+
78
+ context 'config not retrievable' do
79
+ let(:config_server) do
80
+ instance_double(ConfigServer).tap do |config_server|
81
+ expect(ConfigServer).to receive(:new).and_return(config_server)
82
+ end
83
+ end
84
+
85
+ let(:env) { { ERRORS => [] } }
86
+
87
+ context 'first call ever received' do
88
+ it 'raises an error' do
89
+ expected_message = 'error message'
90
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error, expected_message)
91
+ expect { subject.call(env) }.to raise_error(ConfigServer::Error, expected_message)
92
+ end
93
+
94
+ it 'does not write anything to errors' do
95
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error)
96
+
97
+ expect { subject.call(env) }.to raise_error(ConfigServer::Error)
98
+ expect(env[ERRORS]).to be_empty
99
+ end
100
+ end
101
+
102
+ context 'config previously loaded' do
103
+ subject do
104
+ described_class.new(:app, server_url, caching_options: no_caching)
105
+ end
106
+
107
+ let(:response) { [200, {}, []] }
108
+ before do
109
+ app = proc do |_env|
110
+ response
111
+ end
112
+
113
+ expect(config_server).to receive(:get).and_return(config)
114
+ expect(Core).to receive(:from_hash).with(config).and_return(double(build: app))
115
+ subject.call(env)
116
+ end
117
+
118
+ it 'retains the original config' do
119
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error)
120
+ expect(subject.call(env)).to eq(response)
121
+ end
122
+
123
+ it 'writes an error to the error log' do
124
+ expected_error_message = 'error message'
125
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error, expected_error_message)
126
+
127
+ subject.call(env)
128
+ expect(env[ERRORS]).to include(expected_error_message)
129
+ end
130
+ end
131
+ end
61
132
  end
62
133
  end
63
134
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sitehub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.alpha7
4
+ version: 0.5.0.alpha8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ladtech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-03 00:00:00.000000000 Z
11
+ date: 2016-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -346,6 +346,7 @@ files:
346
346
  - lib/sitehub/collection/split_route_collection.rb
347
347
  - lib/sitehub/collection/split_route_collection/split.rb
348
348
  - lib/sitehub/collection_methods.rb
349
+ - lib/sitehub/config_server.rb
349
350
  - lib/sitehub/constants.rb
350
351
  - lib/sitehub/constants/http_header_keys.rb
351
352
  - lib/sitehub/constants/rack_http_header_keys.rb
@@ -390,6 +391,7 @@ files:
390
391
  - sitehub.gemspec
391
392
  - spec/equality_spec.rb
392
393
  - spec/integration/access_logs_spec.rb
394
+ - spec/integration/error_handling_spec.rb
393
395
  - spec/integration/middleware_spec.rb
394
396
  - spec/integration/version_affinity_spec.rb
395
397
  - spec/sitehub/builder_spec.rb
@@ -398,6 +400,7 @@ files:
398
400
  - spec/sitehub/collection/split_route_collection/split_spec.rb
399
401
  - spec/sitehub/collection/split_route_collection_spec.rb
400
402
  - spec/sitehub/collection_spec.rb
403
+ - spec/sitehub/config_server_spec.rb
401
404
  - spec/sitehub/cookie/attribute_spec.rb
402
405
  - spec/sitehub/cookie/flag_spec.rb
403
406
  - spec/sitehub/cookie/rewriting_spec.rb