alephant-broker 1.2.1 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -2
- data/alephant-broker.gemspec +2 -2
- data/lib/alephant/broker.rb +11 -11
- data/lib/alephant/broker/component.rb +20 -67
- data/lib/alephant/broker/component_factory.rb +30 -0
- data/lib/alephant/broker/component_meta.rb +51 -0
- data/lib/alephant/broker/error_component.rb +42 -0
- data/lib/alephant/broker/errors/content_not_found.rb +7 -0
- data/lib/alephant/broker/errors/invalid_cache_key.rb +1 -3
- data/lib/alephant/broker/load_strategy/s3.rb +88 -0
- data/lib/alephant/broker/request/asset.rb +2 -3
- data/lib/alephant/broker/request/batch.rb +4 -5
- data/lib/alephant/broker/request/factory.rb +23 -18
- data/lib/alephant/broker/request/handler.rb +5 -10
- data/lib/alephant/broker/response/asset.rb +17 -10
- data/lib/alephant/broker/response/base.rb +5 -53
- data/lib/alephant/broker/response/batch.rb +12 -9
- data/lib/alephant/broker/response/factory.rb +4 -0
- data/lib/alephant/broker/version.rb +1 -1
- data/spec/fixtures/json/batch.json +1 -0
- data/spec/fixtures/json/batch_compiled.json +1 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/rack_spec.rb +97 -96
- data/spec/spec_helper.rb +4 -0
- metadata +57 -46
@@ -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
|
-
|
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
|
4
|
-
|
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
|
-
|
7
|
-
|
8
|
-
end
|
12
|
+
def self.request_for(load_strategy, env)
|
13
|
+
component_factory = ComponentFactory.new load_strategy
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
15
|
+
@headers = @component.headers
|
16
|
+
@content = @component.content
|
17
|
+
log if @component.is_a? Component
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
19
21
|
|
20
|
-
|
21
|
-
@
|
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
|
-
@
|
19
|
-
@
|
20
|
-
@
|
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 '
|
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
|
-
|
22
|
-
|
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
|
30
|
-
result = components.pmap do |
|
29
|
+
logger.info "Broker: Batch load started (#{batch_id})"
|
30
|
+
result = components.pmap do |component|
|
31
31
|
{
|
32
|
-
'component'
|
33
|
-
'options'
|
34
|
-
|
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
|
39
|
+
logger.info "Broker: Batch load done (#{batch_id})"
|
37
40
|
|
38
41
|
result
|
39
42
|
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
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|