alephant-broker 3.9.2 → 3.10.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.
- checksums.yaml +4 -4
- data/alephant-broker.gemspec +0 -1
- data/lib/alephant/broker/cache.rb +8 -6
- data/lib/alephant/broker/environment.rb +8 -0
- data/lib/alephant/broker/request/batch.rb +6 -7
- data/lib/alephant/broker/request/handler.rb +6 -6
- data/lib/alephant/broker/response/asset.rb +7 -3
- data/lib/alephant/broker/response/base.rb +13 -2
- data/lib/alephant/broker/response/batch.rb +6 -5
- data/lib/alephant/broker/response/factory.rb +3 -3
- data/lib/alephant/broker/version.rb +1 -1
- data/spec/integration/rack_spec.rb +102 -4
- data/spec/spec_helper.rb +6 -2
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 272a9c7f2d5df921c3188da9cf79398f5cf5c8f7
|
4
|
+
data.tar.gz: a3f1f91801351092cf9f8da3e1bc533c69178875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 512e9abbe4ad8299257d01e816268992876ff91ba5abcade3e4d54bf10fb2621ce5ce091f9c6b9ffe9df1e586589e1e099379dfa43d3f188a0f72ce99f3c3d77
|
7
|
+
data.tar.gz: 54e34ee9a11283eb9e7a44f1781564537419036f81a6a81a43f23a5cad48d2bc6c0a0f009bc99cfaae7a6f83f4ec1702b730943050b933159dcd40dd8ba88ff4
|
data/alephant-broker.gemspec
CHANGED
@@ -36,7 +36,6 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_runtime_dependency "alephant-logger", "1.2.0"
|
37
37
|
spec.add_runtime_dependency 'alephant-sequencer'
|
38
38
|
spec.add_runtime_dependency "dalli-elasticache"
|
39
|
-
spec.add_runtime_dependency "pmap"
|
40
39
|
spec.add_runtime_dependency "faraday"
|
41
40
|
spec.add_runtime_dependency "crimp"
|
42
41
|
end
|
@@ -11,19 +11,19 @@ module Alephant
|
|
11
11
|
|
12
12
|
def initialize
|
13
13
|
unless config_endpoint.nil?
|
14
|
-
|
15
|
-
|
14
|
+
@elasticache ||= ::Dalli::ElastiCache.new(config_endpoint, { :expires_in => ttl })
|
15
|
+
@client ||= @elasticache.client
|
16
16
|
else
|
17
17
|
logger.debug "Broker::Cache::Client#initialize: No config endpoint, NullClient used"
|
18
18
|
logger.metric "NoConfigEndpoint"
|
19
|
-
|
19
|
+
@client = NullClient.new
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def get(key, &block)
|
24
24
|
begin
|
25
25
|
versioned_key = versioned key
|
26
|
-
result =
|
26
|
+
result = @client.get versioned_key
|
27
27
|
logger.info "Broker::Cache::Client#get key: #{versioned_key} - #{result ? 'hit' : 'miss'}"
|
28
28
|
logger.metric "GetKeyMiss" unless result
|
29
29
|
result ? result : set(key, block.call)
|
@@ -33,7 +33,7 @@ module Alephant
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def set(key, value, ttl = nil)
|
36
|
-
value.tap { |o|
|
36
|
+
value.tap { |o| @client.set(versioned(key), o, ttl) }
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
@@ -56,7 +56,9 @@ module Alephant
|
|
56
56
|
end
|
57
57
|
|
58
58
|
class NullClient
|
59
|
-
def get(key)
|
59
|
+
def get(key, &block)
|
60
|
+
yield
|
61
|
+
end
|
60
62
|
|
61
63
|
def set(key, value, ttl = nil)
|
62
64
|
value
|
@@ -1,6 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'pmap'
|
1
|
+
require "alephant/logger"
|
2
|
+
require "alephant/broker/component"
|
4
3
|
|
5
4
|
module Alephant
|
6
5
|
module Broker
|
@@ -13,7 +12,7 @@ module Alephant
|
|
13
12
|
def initialize(component_factory, env)
|
14
13
|
logger.info "Request::Batch#initialize: id: #{env.data['batch_id']}"
|
15
14
|
|
16
|
-
@batch_id = env.data[
|
15
|
+
@batch_id = env.data["batch_id"]
|
17
16
|
@component_factory = component_factory
|
18
17
|
@components = components_for env
|
19
18
|
end
|
@@ -21,11 +20,11 @@ module Alephant
|
|
21
20
|
private
|
22
21
|
|
23
22
|
def components_for(env)
|
24
|
-
env.data[
|
23
|
+
env.data["components"].map do |c|
|
25
24
|
@component_factory.create(
|
26
|
-
c[
|
25
|
+
c["component"],
|
27
26
|
batch_id,
|
28
|
-
c[
|
27
|
+
c["options"]
|
29
28
|
)
|
30
29
|
end
|
31
30
|
end
|
@@ -12,16 +12,16 @@ module Alephant
|
|
12
12
|
class Handler
|
13
13
|
extend Logger
|
14
14
|
|
15
|
-
def self.request_for(load_strategy,
|
16
|
-
Request::Factory.request_for(load_strategy,
|
15
|
+
def self.request_for(load_strategy, request_env)
|
16
|
+
Request::Factory.request_for(load_strategy, request_env)
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.response_for(request)
|
20
|
-
Response::Factory.response_for
|
19
|
+
def self.response_for(request, request_env)
|
20
|
+
Response::Factory.response_for(request, request_env)
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.process(load_strategy,
|
24
|
-
response_for
|
23
|
+
def self.process(load_strategy, request_env)
|
24
|
+
response_for(request_for(load_strategy, request_env), request_env)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -6,13 +6,17 @@ module Alephant
|
|
6
6
|
class Asset < Base
|
7
7
|
include Logger
|
8
8
|
|
9
|
-
def initialize(component)
|
9
|
+
def initialize(component, request_env)
|
10
10
|
@component = component
|
11
|
-
|
11
|
+
|
12
|
+
@status = component_not_modified(@component.headers, request_env) ? 304 : component.status
|
13
|
+
|
14
|
+
super @status
|
15
|
+
|
16
|
+
@headers.merge!(@component.headers)
|
12
17
|
end
|
13
18
|
|
14
19
|
def setup
|
15
|
-
@headers.merge!(@component.headers)
|
16
20
|
@content = @component.content
|
17
21
|
log if @component.is_a? Component
|
18
22
|
end
|
@@ -14,6 +14,7 @@ module Alephant
|
|
14
14
|
|
15
15
|
STATUS_CODE_MAPPING = {
|
16
16
|
200 => "ok",
|
17
|
+
304 => "",
|
17
18
|
404 => "Not found",
|
18
19
|
500 => "Error retrieving content"
|
19
20
|
}
|
@@ -24,8 +25,8 @@ module Alephant
|
|
24
25
|
@headers.merge!(Broker.config[:headers]) if Broker.config.has_key?(:headers)
|
25
26
|
@status = status
|
26
27
|
|
27
|
-
add_no_cache_headers if status
|
28
|
-
setup
|
28
|
+
add_no_cache_headers if should_add_no_cache_headers?(status)
|
29
|
+
setup if status == 200
|
29
30
|
end
|
30
31
|
|
31
32
|
protected
|
@@ -34,6 +35,10 @@ module Alephant
|
|
34
35
|
|
35
36
|
private
|
36
37
|
|
38
|
+
def should_add_no_cache_headers?(status)
|
39
|
+
status != 200 && status != 304
|
40
|
+
end
|
41
|
+
|
37
42
|
def add_no_cache_headers
|
38
43
|
headers.merge!(
|
39
44
|
"Cache-Control" => "no-cache, must-revalidate",
|
@@ -43,6 +48,12 @@ module Alephant
|
|
43
48
|
log
|
44
49
|
end
|
45
50
|
|
51
|
+
def component_not_modified(headers, request_env)
|
52
|
+
return false if headers["Last-Modified"].nil? && headers["ETag"].nil?
|
53
|
+
|
54
|
+
headers["Last-Modified"] == request_env.if_modified_since || headers["ETag"] == request_env.if_none_match
|
55
|
+
end
|
56
|
+
|
46
57
|
def log
|
47
58
|
logger.metric "BrokerNon200Response#{status}"
|
48
59
|
end
|
@@ -9,11 +9,14 @@ module Alephant
|
|
9
9
|
|
10
10
|
attr_reader :components, :batch_id
|
11
11
|
|
12
|
-
def initialize(components, batch_id)
|
12
|
+
def initialize(components, batch_id, request_env)
|
13
13
|
@components = components
|
14
14
|
@batch_id = batch_id
|
15
|
+
@status = component_not_modified(batch_response_headers, request_env) ? 304 : 200
|
15
16
|
|
16
|
-
super(
|
17
|
+
super(@status, "application/json")
|
18
|
+
|
19
|
+
@headers.merge!(batch_response_headers)
|
17
20
|
end
|
18
21
|
|
19
22
|
def setup
|
@@ -21,8 +24,6 @@ module Alephant
|
|
21
24
|
'batch_id' => batch_id,
|
22
25
|
'components' => json
|
23
26
|
})
|
24
|
-
|
25
|
-
@headers.merge!(batch_response_headers)
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
@@ -53,7 +54,7 @@ module Alephant
|
|
53
54
|
def batch_response_etag
|
54
55
|
etags = components.map do |component|
|
55
56
|
component.headers["ETag"]
|
56
|
-
end.compact
|
57
|
+
end.compact.sort
|
57
58
|
|
58
59
|
Crimp.signature(etags)
|
59
60
|
end
|
@@ -4,12 +4,12 @@ module Alephant
|
|
4
4
|
module Broker
|
5
5
|
module Response
|
6
6
|
class Factory
|
7
|
-
def self.response_for(request)
|
7
|
+
def self.response_for(request, request_env)
|
8
8
|
case request
|
9
9
|
when Request::Asset
|
10
|
-
Asset.new(request.component)
|
10
|
+
Asset.new(request.component, request_env)
|
11
11
|
when Request::Batch
|
12
|
-
Batch.new(request.components, request.batch_id)
|
12
|
+
Batch.new(request.components, request.batch_id, request_env)
|
13
13
|
when Request::Status
|
14
14
|
Status.new
|
15
15
|
else
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require_relative "spec_helper"
|
2
|
+
require "alephant/broker"
|
2
3
|
|
3
4
|
describe Alephant::Broker::Application do
|
4
5
|
include Rack::Test::Methods
|
6
|
+
|
5
7
|
let(:options) do
|
6
8
|
{
|
7
9
|
:lookup_table_name => "test_table",
|
@@ -16,16 +18,18 @@ describe Alephant::Broker::Application do
|
|
16
18
|
options
|
17
19
|
)
|
18
20
|
end
|
21
|
+
|
19
22
|
let(:content) do
|
20
23
|
AWS::Core::Data.new(
|
21
24
|
:content_type => "test/content",
|
22
25
|
:content => "Test",
|
23
26
|
:meta => {
|
24
|
-
|
25
|
-
|
27
|
+
"head_ETag" => "123",
|
28
|
+
"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
|
26
29
|
}
|
27
30
|
)
|
28
31
|
end
|
32
|
+
|
29
33
|
let(:sequencer_double) do
|
30
34
|
instance_double(
|
31
35
|
"Alephant::Sequencer::Sequencer",
|
@@ -90,6 +94,27 @@ describe Alephant::Broker::Application do
|
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
97
|
+
describe "single component not modified response" do
|
98
|
+
before do
|
99
|
+
allow(Alephant::Storage).to receive(:new) { s3_double }
|
100
|
+
get(
|
101
|
+
"/component/test_component",
|
102
|
+
{},
|
103
|
+
{
|
104
|
+
"IF_MODIFIED_SINCE" => "Mon, 11 Apr 2016 10:39:57 GMT"
|
105
|
+
}
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
specify { expect(last_response.status).to eql 304 }
|
110
|
+
specify { expect(last_response.body).to eql "" }
|
111
|
+
specify { expect(last_response.headers).to_not include("Cache-Control") }
|
112
|
+
specify { expect(last_response.headers).to_not include("Pragma") }
|
113
|
+
specify { expect(last_response.headers).to_not include("Expires") }
|
114
|
+
specify { expect(last_response.headers["ETag"]).to eq("123") }
|
115
|
+
specify { expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT") }
|
116
|
+
end
|
117
|
+
|
93
118
|
describe "Components endpoint '/components'" do
|
94
119
|
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
95
120
|
let(:batch_json) do
|
@@ -98,9 +123,10 @@ describe Alephant::Broker::Application do
|
|
98
123
|
let(:batch_compiled_json) do
|
99
124
|
IO.read("#{fixture_path}/batch_compiled.json").strip
|
100
125
|
end
|
126
|
+
let(:s3_double_batch) { instance_double("Alephant::Storage") }
|
101
127
|
|
102
128
|
before do
|
103
|
-
allow(
|
129
|
+
allow(s3_double_batch).to receive(:get).and_return(
|
104
130
|
content,
|
105
131
|
AWS::Core::Data.new(
|
106
132
|
:content_type => "test/content",
|
@@ -112,7 +138,7 @@ describe Alephant::Broker::Application do
|
|
112
138
|
)
|
113
139
|
)
|
114
140
|
|
115
|
-
allow(Alephant::Storage).to receive(:new) {
|
141
|
+
allow(Alephant::Storage).to receive(:new) { s3_double_batch }
|
116
142
|
end
|
117
143
|
|
118
144
|
context "when using valid batch asset data" do
|
@@ -141,6 +167,78 @@ describe Alephant::Broker::Application do
|
|
141
167
|
end
|
142
168
|
end
|
143
169
|
|
170
|
+
describe "Components unmodified '/components' response" do
|
171
|
+
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
172
|
+
let(:batch_json) { IO.read("#{fixture_path}/batch.json").strip }
|
173
|
+
let(:batch_compiled_json) { IO.read("#{fixture_path}/batch_compiled.json").strip }
|
174
|
+
let(:s3_double_with_etag) { instance_double("Alephant::Storage") }
|
175
|
+
let(:lookup_location_double_for_options_request) do
|
176
|
+
instance_double("Alephant::Lookup::Location", :location => "test/location/with/options")
|
177
|
+
end
|
178
|
+
|
179
|
+
before do
|
180
|
+
allow(lookup_helper_double).to receive(:read)
|
181
|
+
.with("ni_council_results_table", { :foo => "bar" }, "111")
|
182
|
+
.and_return(lookup_location_double_for_options_request)
|
183
|
+
|
184
|
+
allow(s3_double_with_etag).to receive(:get)
|
185
|
+
.with("test/location")
|
186
|
+
.and_return(
|
187
|
+
content
|
188
|
+
)
|
189
|
+
|
190
|
+
allow(s3_double_with_etag).to receive(:get)
|
191
|
+
.with("test/location/with/options")
|
192
|
+
.and_return(
|
193
|
+
AWS::Core::Data.new(
|
194
|
+
:content_type => "test/content",
|
195
|
+
:content => "Test",
|
196
|
+
:meta => {
|
197
|
+
"head_ETag" => "abc",
|
198
|
+
"head_Last-Modified" => "Mon, 11 Apr 2016 09:39:57 GMT"
|
199
|
+
}
|
200
|
+
)
|
201
|
+
)
|
202
|
+
|
203
|
+
allow(Alephant::Storage).to receive(:new) { s3_double_with_etag }
|
204
|
+
end
|
205
|
+
|
206
|
+
context "when requesting an unmodified response" do
|
207
|
+
let(:path) { "/components/batch" }
|
208
|
+
let(:content_type) { "application/json" }
|
209
|
+
let(:etag) { "34774567db979628363e6e865127623f" }
|
210
|
+
|
211
|
+
before do
|
212
|
+
post(path, batch_json,
|
213
|
+
"CONTENT_TYPE" => content_type,
|
214
|
+
"IF_NONE_MATCH" => etag)
|
215
|
+
end
|
216
|
+
|
217
|
+
specify { expect(last_response.status).to eql 304 }
|
218
|
+
specify { expect(last_response.body).to eq "" }
|
219
|
+
|
220
|
+
describe "response should have headers" do
|
221
|
+
it "should NOT have a Content-Type header" do
|
222
|
+
expect(last_response.headers).to_not include("Content-Type")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should have ETag cache header" do
|
226
|
+
expect(last_response.headers["ETag"]).to eq(etag)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should have most recent Last-Modified header" do
|
230
|
+
expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT")
|
231
|
+
end
|
232
|
+
|
233
|
+
it "shoud not have no cache headers" do
|
234
|
+
expect(last_response.headers).to_not include("Cache-Control")
|
235
|
+
expect(last_response.headers).to_not include("Pragma")
|
236
|
+
expect(last_response.headers).to_not include("Expires")
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
144
242
|
describe "S3 headers" do
|
145
243
|
let(:content) do
|
146
244
|
AWS::Core::Data.new(
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,10 @@ require "alephant/broker/load_strategy/http"
|
|
9
9
|
require "alephant/broker/cache"
|
10
10
|
require "alephant/broker/errors/content_not_found"
|
11
11
|
|
12
|
-
require
|
12
|
+
require "rack/test"
|
13
13
|
|
14
|
-
ENV['RACK_ENV'] =
|
14
|
+
ENV['RACK_ENV'] = "test"
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.order = "random"
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alephant-broker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BBC News
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -234,20 +234,6 @@ dependencies:
|
|
234
234
|
- - '>='
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: '0'
|
237
|
-
- !ruby/object:Gem::Dependency
|
238
|
-
requirement: !ruby/object:Gem::Requirement
|
239
|
-
requirements:
|
240
|
-
- - '>='
|
241
|
-
- !ruby/object:Gem::Version
|
242
|
-
version: '0'
|
243
|
-
name: pmap
|
244
|
-
prerelease: false
|
245
|
-
type: :runtime
|
246
|
-
version_requirements: !ruby/object:Gem::Requirement
|
247
|
-
requirements:
|
248
|
-
- - '>='
|
249
|
-
- !ruby/object:Gem::Version
|
250
|
-
version: '0'
|
251
237
|
- !ruby/object:Gem::Dependency
|
252
238
|
requirement: !ruby/object:Gem::Requirement
|
253
239
|
requirements:
|