alephant-broker 3.9.2 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3551ebe253cb1eb4939a53c0175a486e018aa640
4
- data.tar.gz: 112525df499493ea48bbaf9c7c7d7a5aa99c65b9
3
+ metadata.gz: 272a9c7f2d5df921c3188da9cf79398f5cf5c8f7
4
+ data.tar.gz: a3f1f91801351092cf9f8da3e1bc533c69178875
5
5
  SHA512:
6
- metadata.gz: e99f9ffabc1aeead26a0ee7d8cab8e363b2d8e7274a3b544e0aca9916ecb5157c3aed074c5989523fd97dbf49959a3c442a8276fd53fb03eee0dbdd9d5e3bff6
7
- data.tar.gz: 7d70165c6a1fdc5a31ca876a744b0d55b3bc62fe5d542fd8d5ad6b63a7f24fb131896958ee2d47bb578816c883d8b643dbf6dbe5faebdd0cf7dc398fa51eed51
6
+ metadata.gz: 512e9abbe4ad8299257d01e816268992876ff91ba5abcade3e4d54bf10fb2621ce5ce091f9c6b9ffe9df1e586589e1e099379dfa43d3f188a0f72ce99f3c3d77
7
+ data.tar.gz: 54e34ee9a11283eb9e7a44f1781564537419036f81a6a81a43f23a5cad48d2bc6c0a0f009bc99cfaae7a6f83f4ec1702b730943050b933159dcd40dd8ba88ff4
@@ -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
- @@elasticache ||= ::Dalli::ElastiCache.new(config_endpoint, { :expires_in => ttl })
15
- @@client ||= @@elasticache.client
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
- @@client = NullClient.new
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 = @@client.get versioned_key
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| @@client.set(versioned(key), o, ttl) }
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); end
59
+ def get(key, &block)
60
+ yield
61
+ end
60
62
 
61
63
  def set(key, value, ttl = nil)
62
64
  value
@@ -32,6 +32,14 @@ module Alephant
32
32
  settings['PATH_INFO']
33
33
  end
34
34
 
35
+ def if_none_match
36
+ settings["IF_NONE_MATCH"]
37
+ end
38
+
39
+ def if_modified_since
40
+ settings["IF_MODIFIED_SINCE"]
41
+ end
42
+
35
43
  def data
36
44
  parse(rack_input) if post?
37
45
  end
@@ -1,6 +1,5 @@
1
- require 'alephant/logger'
2
- require 'alephant/broker/component'
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['batch_id']
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['components'].pmap do |c|
23
+ env.data["components"].map do |c|
25
24
  @component_factory.create(
26
- c['component'],
25
+ c["component"],
27
26
  batch_id,
28
- c['options']
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, env)
16
- Request::Factory.request_for(load_strategy, env)
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 request
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, env)
24
- response_for request_for(load_strategy, env)
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
- super component.status
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 != 200
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(200, "application/json")
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,5 +1,5 @@
1
1
  module Alephant
2
2
  module Broker
3
- VERSION = "3.9.2"
3
+ VERSION = "3.10.0"
4
4
  end
5
5
  end
@@ -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
- :head_ETag => "123",
25
- :"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
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(s3_double).to receive(:get).and_return(
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) { s3_double }
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 'rack/test'
12
+ require "rack/test"
13
13
 
14
- ENV['RACK_ENV'] = 'test'
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.9.2
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-14 00:00:00.000000000 Z
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: