alephant-broker 1.2.1 → 1.3.1

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,18 +1,18 @@
1
1
  require 'alephant/logger'
2
2
  require 'alephant/broker/component'
3
3
 
4
-
5
4
  module Alephant
6
5
  module Broker
7
6
  module Request
8
7
  class Batch
9
8
  include Logger
10
9
 
11
- attr_reader :batch_id, :components
10
+ attr_reader :batch_id, :components, :load_strategy
12
11
 
13
- def initialize(env)
12
+ def initialize(component_factory, env)
14
13
  logger.debug("Request::Batch#initialize(#{env.settings})")
15
14
 
15
+ @component_factory = component_factory
16
16
  @batch_id = env.data['batch_id']
17
17
  @components = components_for env
18
18
 
@@ -23,14 +23,13 @@ module Alephant
23
23
 
24
24
  def components_for(env)
25
25
  env.data['components'].map do |c|
26
- Component.new(
26
+ @component_factory.create(
27
27
  c['component'],
28
28
  batch_id,
29
29
  c['options']
30
30
  )
31
31
  end
32
32
  end
33
-
34
33
  end
35
34
  end
36
35
  end
@@ -1,26 +1,31 @@
1
1
  require 'alephant/broker/request'
2
+ require 'alephant/broker/component_factory'
2
3
 
3
- module Alephant::Broker::Request
4
- class Factory
4
+ module Alephant
5
+ module Broker
6
+ module Request
7
+ class Factory
8
+ def self.request_type_from(env)
9
+ env.path.split('/')[1]
10
+ end
5
11
 
6
- def self.request_type_from(env)
7
- env.path.split('/')[1]
8
- end
12
+ def self.request_for(load_strategy, env)
13
+ component_factory = ComponentFactory.new load_strategy
9
14
 
10
- def self.request_for(env)
11
- case request_type_from(env)
12
- when 'multi'
13
- Multi.new(env)
14
- when 'component'
15
- Asset.new(env)
16
- when 'components'
17
- Batch.new(env)
18
- when 'status'
19
- Status.new
20
- else
21
- NotFound.new
15
+ case request_type_from(env)
16
+ when 'multi'
17
+ Multi.new(env)
18
+ when 'component'
19
+ Asset.new(component_factory, env)
20
+ when 'components'
21
+ Batch.new(component_factory, env)
22
+ when 'status'
23
+ Status.new
24
+ else
25
+ NotFound.new
26
+ end
27
+ end
22
28
  end
23
29
  end
24
30
  end
25
31
  end
26
-
@@ -4,6 +4,7 @@ require 'alephant/broker/request'
4
4
  require 'alephant/broker/response'
5
5
  require 'alephant/broker/request/factory'
6
6
  require 'alephant/broker/response/factory'
7
+ require 'alephant/broker/errors/content_not_found'
7
8
 
8
9
  module Alephant
9
10
  module Broker
@@ -11,23 +12,17 @@ module Alephant
11
12
  class Handler
12
13
  extend Logger
13
14
 
14
- def self.request_for(env)
15
- Request::Factory.request_for env
15
+ def self.request_for(load_strategy, env)
16
+ Request::Factory.request_for(load_strategy, env)
16
17
  end
17
18
 
18
19
  def self.response_for(request)
19
20
  Response::Factory.response_for request
20
21
  end
21
22
 
22
- def self.process(env)
23
- begin
24
- response_for request_for(env)
25
- rescue Exception => e
26
- logger.warn("Broker.requestHandler.process: Exception raised (#{e.message}, #{e.backtrace.join('\n')})")
27
- Response::Factory.error
28
- end
23
+ def self.process(load_strategy, env)
24
+ response_for request_for(load_strategy, env)
29
25
  end
30
-
31
26
  end
32
27
  end
33
28
  end
@@ -1,4 +1,3 @@
1
- require 'alephant/broker/errors/invalid_cache_key'
2
1
  require 'alephant/logger'
3
2
 
4
3
  module Alephant
@@ -7,23 +6,31 @@ module Alephant
7
6
  class Asset < Base
8
7
  include Logger
9
8
 
10
- attr_reader :component
11
-
12
9
  def initialize(component)
13
10
  @component = component
14
- super()
11
+ super component.status
15
12
  end
16
13
 
17
14
  def setup
18
- loaded_content = load(component)
15
+ @headers = @component.headers
16
+ @content = @component.content
17
+ log if @component.is_a? Component
18
+ end
19
+
20
+ private
19
21
 
20
- @content = loaded_content[:body]
21
- @content_type = loaded_content[:content_type]
22
- @status = loaded_content[:status]
23
- @sequence = component.version.nil? ? 'not available' : component.version
24
- @cached = component.cached
22
+ def batched
23
+ @component.batch_id.nil? ? '' : 'batched'
25
24
  end
26
25
 
26
+ def details
27
+ c = @component
28
+ "#{c.id}/#{c.opts_hash}/#{c.headers} #{batched} #{c.options}"
29
+ end
30
+
31
+ def log
32
+ logger.info "Broker: Component loaded! #{details} (200)"
33
+ end
27
34
  end
28
35
  end
29
36
  end
@@ -1,3 +1,4 @@
1
+ require 'alephant/broker/errors/invalid_cache_key'
1
2
  require 'aws-sdk'
2
3
  require 'ostruct'
3
4
 
@@ -5,8 +6,7 @@ module Alephant
5
6
  module Broker
6
7
  module Response
7
8
  class Base
8
- attr_reader :headers
9
- attr_accessor :status, :content, :content_type, :version, :sequence, :cached
9
+ attr_reader :content, :headers, :status
10
10
 
11
11
  STATUS_CODE_MAPPING = {
12
12
  200 => 'ok',
@@ -15,64 +15,16 @@ module Alephant
15
15
  }
16
16
 
17
17
  def initialize(status = 200, content_type = "text/html")
18
- @headers = {}
19
- @sequence = 'not available'
20
- @version = Broker.config.fetch('elasticache_cache_version', 'not available').to_s
21
- @cached = false
22
- @content_type = content_type
23
- @status = status
24
- @content = STATUS_CODE_MAPPING[status]
18
+ @content = STATUS_CODE_MAPPING[status]
19
+ @headers = { "Content-Type" => content_type }
20
+ @status = status
25
21
 
26
22
  setup
27
23
  end
28
24
 
29
- def to_h
30
- {
31
- :status => @status,
32
- :content => @content,
33
- :content_type => @content_type,
34
- :version => @version,
35
- :sequence => @sequence,
36
- :cached => @cached
37
- }
38
- end
39
-
40
25
  protected
41
26
 
42
27
  def setup; end
43
-
44
- def load(component)
45
- begin
46
-
47
- data = OpenStruct.new(:status => 200, :content_type => content_type)
48
- component.load
49
-
50
- data.content_type = component.content_type
51
- data.body = component.content.force_encoding('UTF-8')
52
- rescue AWS::S3::Errors::NoSuchKey, InvalidCacheKey => e
53
- data.body = "Not found"
54
- data.status = 404
55
- rescue StandardError => e
56
- data.body = "#{error_for(e)}"
57
- data.status = 500
58
- end
59
-
60
- log(component, data.status, e)
61
- data.marshal_dump
62
- end
63
-
64
- def log(c, status, e = nil)
65
- logger.info("Broker: Component loaded: #{details_for(c)} (#{status}) #{error_for(e)}")
66
- end
67
-
68
- def details_for(c)
69
- "#{c.id}/#{c.opts_hash}/#{c.version} #{c.batch_id.nil? ? '' : "batched"} (#{c.options})"
70
- end
71
-
72
- def error_for(e)
73
- e.nil? ? nil : "#{e.message}\n#{e.backtrace.join('\n')}"
74
- end
75
-
76
28
  end
77
29
  end
78
30
  end
@@ -1,5 +1,5 @@
1
1
  require 'alephant/logger'
2
- require 'peach'
2
+ require 'pmap'
3
3
 
4
4
  module Alephant
5
5
  module Broker
@@ -18,22 +18,25 @@ module Alephant
18
18
 
19
19
  def setup
20
20
  @content = JSON.generate({
21
- "batch_id" => batch_id,
22
- "components" => json
21
+ 'batch_id' => batch_id,
22
+ 'components' => json
23
23
  })
24
24
  end
25
25
 
26
26
  private
27
27
 
28
28
  def json
29
- logger.info("Broker: Batch load started (#{batch_id})")
30
- result = components.pmap do | component |
29
+ logger.info "Broker: Batch load started (#{batch_id})"
30
+ result = components.pmap do |component|
31
31
  {
32
- 'component' => component.id,
33
- 'options' => component.options
34
- }.merge load(component)
32
+ 'component' => component.id,
33
+ 'options' => component.options,
34
+ 'status' => component.status,
35
+ 'content_type' => component.content_type,
36
+ 'body' => component.content
37
+ }
35
38
  end
36
- logger.info("Broker: Batch load done (#{batch_id})")
39
+ logger.info "Broker: Batch load done (#{batch_id})"
37
40
 
38
41
  result
39
42
  end
@@ -22,6 +22,10 @@ module Alephant
22
22
  def self.error
23
23
  ServerError.new
24
24
  end
25
+
26
+ def self.not_found
27
+ NotFound.new
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,5 +1,5 @@
1
1
  module Alephant
2
2
  module Broker
3
- VERSION = "1.2.1"
3
+ VERSION = "1.3.1"
4
4
  end
5
5
  end
@@ -0,0 +1 @@
1
+ {"batch_id":"baz","components":[{"component":"ni_council_results_table"},{"component":"ni_council_results_table"}]}
@@ -0,0 +1 @@
1
+ {"batch_id":"baz","components":[{"component":"ni_council_results_table","options":{},"status":200,"content_type":"foo/bar","body":"Test"},{"component":"ni_council_results_table","options":{},"status":200,"content_type":"foo/bar","body":"Test"}]}
@@ -0,0 +1 @@
1
+ require_relative '../spec_helper'
data/spec/rack_spec.rb CHANGED
@@ -1,23 +1,37 @@
1
- ENV['RACK_ENV'] = 'test'
2
-
3
1
  require 'spec_helper'
4
- require 'rack/test'
5
- require 'alephant/broker'
6
-
7
- RSpec.configure do |conf|
8
- conf.include Rack::Test::Methods
9
- end
10
2
 
11
- describe 'Broker Rack Application' do
12
- before do
13
- cache_hash = {
3
+ describe Alephant::Broker::Application do
4
+ include Rack::Test::Methods
5
+
6
+ let(:app) do
7
+ described_class.new(
8
+ Alephant::Broker::LoadStrategy::S3.new,
9
+ {
10
+ :lookup_table_name => 'test_table',
11
+ :bucket_id => 'test_bucket',
12
+ :path => 'bucket_path'
13
+ }
14
+ )
15
+ end
16
+ let(:cache_hash) do
17
+ {
14
18
  :content_type => 'test/content',
15
19
  :content => 'Test'
16
20
  }
21
+ end
22
+ let(:sequencer_double) do
23
+ instance_double(
24
+ 'Alephant::Sequencer::Sequencer',
25
+ :get_last_seen => '111'
26
+ )
27
+ end
28
+
29
+ before do
30
+ allow_any_instance_of(Logger).to receive(:info)
31
+ allow_any_instance_of(Logger).to receive(:debug)
17
32
 
18
33
  allow_any_instance_of(Alephant::Broker::Cache::Client)
19
- .to receive(:get)
20
- .and_return(cache_hash)
34
+ .to receive(:get).and_return(cache_hash)
21
35
 
22
36
  allow_any_instance_of(Alephant::Broker::Component)
23
37
  .to receive_messages(
@@ -27,97 +41,84 @@ describe 'Broker Rack Application' do
27
41
  )
28
42
 
29
43
  allow_any_instance_of(Alephant::Broker::Response::Asset)
30
- .to receive(:status)
31
- .and_return(200)
32
- end
33
-
34
- def app
35
- Alephant::Broker::Application.new({
36
- :lookup_table_name => 'test_table',
37
- :bucket_id => 'test_bucket',
38
- :path => 'bucket_path'
39
- })
40
- end
41
-
42
- it 'Tests status page' do
43
- get '/status'
44
- expect(last_response).to be_ok
45
- expect(last_response.body).to eq('ok')
46
- end
47
-
48
- it "Tests not found page" do
49
- get '/some/non-existent-page'
50
- expect(last_response.status).to eq(404)
51
- expect(last_response.body).to eq('Not found')
52
- end
53
-
54
- it "Test asset data is returned" do
55
- get '/component/test_component'
56
-
57
- expect(last_response).to be_ok
58
- expect(last_response.body).to eq('Test')
59
- end
44
+ .to receive(:status).and_return(200)
60
45
 
61
- it "Tests query string parameters are passed correctly to lookup" do
62
- get '/component/test_component?variant=test_variant'
63
-
64
- expect(last_response).to be_ok
65
- expect(last_response.body).to eq('Test')
46
+ allow(Alephant::Sequencer).to receive(:create) { sequencer_double }
66
47
  end
67
48
 
68
- it "Tests 404 when lookup doesn't return a valid location" do
69
- allow_any_instance_of(Alephant::Broker::Response::Asset)
70
- .to receive(:status)
71
- .and_return(404)
72
-
73
- get '/component/test_component'
74
-
75
- expect(last_response.status).to eq(404)
49
+ describe 'Status endpoint `/status`' do
50
+ before { get '/status' }
51
+ specify { expect(last_response.status).to eql 200 }
52
+ specify { expect(last_response.body).to eql 'ok' }
76
53
  end
77
54
 
78
- it "Tests 500 when exception is raised in application" do
79
- allow_any_instance_of(Alephant::Broker::Response::Asset)
80
- .to receive(:status)
81
- .and_return(500)
82
-
83
- get '/component/test_component'
84
-
85
- expect(last_response.status).to eq(500)
55
+ describe '404 endpoint `/banana`' do
56
+ before { get '/banana' }
57
+ specify { expect(last_response.status).to eql 404 }
58
+ specify { expect(last_response.body).to eq 'Not found' }
86
59
  end
87
60
 
88
- it "Test batch asset data is returned" do
89
- json = '{"batch_id":"baz","components":[{"component":"ni_council_results_table"},{"component":"ni_council_results_table"}]}'
90
- compiled_json = '{"batch_id":"baz","components":[{"component":"ni_council_results_table","options":{},"status":200,"content_type":"foo/bar","body":"Test"},{"component":"ni_council_results_table","options":{},"status":200,"content_type":"foo/bar","body":"Test"}]}'
91
-
92
- post '/components/batch', json, "CONTENT_TYPE" => "application/json"
93
-
94
- expect(last_response).to be_ok
95
- expect(last_response.body).to eq(compiled_json)
61
+ describe 'Component endpoint `/component/...`' do
62
+ let(:batch_json) do
63
+ IO.read("#{File.dirname(__FILE__)}/fixtures/json/batch.json").strip
64
+ end
65
+ let(:batch_compiled_json) do
66
+ IO.read("#{File.dirname(__FILE__)}/fixtures/json/batch_compiled.json").strip
67
+ end
68
+
69
+ context 'for a valid component ID' do
70
+ before { get '/component/test_component' }
71
+ specify { expect(last_response.status).to eql 200 }
72
+ specify { expect(last_response.body).to eql 'Test' }
73
+ end
74
+
75
+ context 'for valid URL parameters in request' do
76
+ before { get '/component/test_component?variant=test_variant' }
77
+ specify { expect(last_response.status).to eq 200 }
78
+ specify { expect(last_response.body).to eq 'Test' }
79
+ end
80
+
81
+ context 'when using valid batch asset data' do
82
+ before { post '/components/batch', batch_json, 'CONTENT_TYPE' => 'application/json' }
83
+ specify { expect(last_response.status).to eql 200 }
84
+ specify { expect(JSON.parse last_response.body).to eq JSON.parse(batch_compiled_json) }
85
+ end
96
86
  end
97
87
 
98
- it "Should handle old cache data gracefully" do
99
- lookup_location_double = double('Alephant::Lookup::Location', :location => 'test/location')
100
- lookup_helper_double = double('Alephant::Lookup::LookupHelper', :read => lookup_location_double)
101
-
102
- cache_double = double('Alephant::Broker::Cache::Client', :set => { :content_type => 'test/html', :content => '<p>Some data</p>' }, :get => '<p>Some data</p>')
103
- s3_cache_double = double('Alephant::Cache', :get => 'test_content')
104
-
105
- allow(Alephant::Lookup)
106
- .to receive(:create)
107
- .and_return(lookup_helper_double)
108
-
109
- allow(Alephant::Broker::Cache::Client)
110
- .to receive(:new)
111
- .and_return(cache_double)
112
-
113
- allow(Alephant::Cache)
114
- .to receive(:new)
115
- .and_return(s3_cache_double)
116
-
117
- expect(cache_double).to receive(:set).once
118
-
119
- get '/component/test_component'
120
-
88
+ describe 'Cached data' do
89
+ let(:cache_double) do
90
+ instance_double(
91
+ 'Alephant::Broker::Cache::Client',
92
+ :set => {
93
+ :content_type => 'test/html',
94
+ :content => '<p>Some data</p>'
95
+ },
96
+ :get => '<p>Some data</p>'
97
+ )
98
+ end
99
+ let(:lookup_location_double) do
100
+ instance_double('Alephant::Lookup::Location', location: 'test/location')
101
+ end
102
+ let(:lookup_helper_double) do
103
+ instance_double('Alephant::Lookup::LookupHelper', read: lookup_location_double)
104
+ end
105
+ let(:s3_cache_double) do
106
+ instance_double(
107
+ 'Alephant::Cache',
108
+ :get => 'test_content'
109
+ )
110
+ end
111
+
112
+ context 'which is old' do
113
+ before do
114
+ allow(Alephant::Lookup).to receive(:create) { lookup_helper_double }
115
+ allow(Alephant::Broker::Cache::Client).to receive(:new) { cache_double }
116
+ allow(Alephant::Cache).to receive(:new) { s3_cache_double }
117
+ end
118
+ it 'should update the cache (call `.set`)' do
119
+ expect(cache_double).to receive(:set).once
120
+ end
121
+ after { get '/component/test_component' }
122
+ end
121
123
  end
122
-
123
124
  end