alephant-storage 1.1.1 → 2.0.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: a67098b9f5dd6561721bfb134d95afe3466235e1
4
- data.tar.gz: 26ca720ff670c24cb6af6da20c036a2f4c39fc07
3
+ metadata.gz: 511f0d39c816881993bc6677398496356b367a4c
4
+ data.tar.gz: 90a2236c5d3f49caa4c0f1f26d64b36d96d75f40
5
5
  SHA512:
6
- metadata.gz: ad8005de05e06602ad9f3bba4f63484ad0fc54c5eefc39b8e800f80f8072b916aa46f39d54ba0898b31103fa666227bfea334a05b482aaca111f1507334de673
7
- data.tar.gz: 78c192408a5b3c7286253ee876c31161a5e86c1764f7f2d8941b30f3b247d21880bed9fe6f13950eb7f8858fda84f98057c4ef2531620099e9a80356ff60ca06
6
+ metadata.gz: e8517904fc38cacfd6d2dea8e7bbe7d35cf0daa749c7676f962fa29fc55033b1142df388c0e67ec14f675d0e7dc2ecae35c373b4fff0a2df17516ec7e86136bc
7
+ data.tar.gz: 6ff74d1808163c5323f60b1351ab983c4fbe3cd26dbe2fc036af9522560eb15efa598311a9912ba7b902e4c5d8c687d25a0a539662770cc0f064fd604f42fa94
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  Gemfile.lock
4
4
  .rspec
5
5
  *.gem
6
+ *.log
6
7
 
7
8
  /pkg
8
9
  /tmp
@@ -1 +1 @@
1
- jruby-1.7.17
1
+ 2.3
@@ -1,9 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - "2.3"
3
4
  - "jruby"
4
5
  notifications:
5
6
  email:
6
7
  recipients:
7
- - DigitalNewsFrameworksTeam@bbc.co.uk
8
+ - D&ENewsFrameworksTeam@bbc.co.uk
8
9
  on_failure: change
9
10
  on_success: never
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "alephant-storage"
8
8
  spec.version = Alephant::Storage::VERSION
9
9
  spec.authors = ["BBC News"]
10
- spec.email = ["FutureMediaNewsRubyGems@bbc.co.uk"]
10
+ spec.email = ["D&ENewsFrameworksTeam@bbc.co.uk"]
11
11
  spec.summary = "Simple abstraction layer over S3 for get/put."
12
12
  spec.homepage = "https://github.com/BBC-News/alephant-storage"
13
13
  spec.license = "MIT"
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "bundler", "~> 1.5"
28
28
  spec.add_development_dependency "rake"
29
29
 
30
- spec.add_runtime_dependency 'aws-sdk', '~> 1.0'
30
+ spec.add_runtime_dependency "aws-sdk-s3"
31
31
  spec.add_runtime_dependency 'alephant-logger'
32
32
  end
@@ -1,73 +1,88 @@
1
1
  require "alephant/storage/version"
2
2
  require "alephant/logger"
3
- require "aws-sdk"
3
+ require "aws-sdk-s3"
4
4
  require "date"
5
5
 
6
6
  module Alephant
7
7
  class Storage
8
8
  include Logger
9
- attr_reader :id, :bucket, :path
9
+ attr_reader :bucket, :path
10
10
 
11
- def initialize(id, path)
12
- @id = id
13
- @path = path
14
- @bucket = AWS::S3.new.buckets[id]
11
+ def initialize(bucket, path)
12
+ @bucket = bucket
13
+ @path = path
15
14
 
16
15
  logger.info(
17
16
  "event" => "StorageInitialized",
18
- "id" => id,
17
+ "bucket" => bucket,
19
18
  "path" => path,
20
19
  "method" => "#{self.class}#initialize"
21
20
  )
22
21
  end
23
22
 
24
23
  def clear
25
- bucket.objects.with_prefix(path).delete_all
26
24
  logger.info(
27
25
  "event" => "StorageCleared",
26
+ "bucket" => bucket,
28
27
  "path" => path,
29
28
  "method" => "#{self.class}#clear"
30
29
  )
31
- end
32
30
 
33
- def put(id, data, content_type = "text/plain", meta = {})
34
- bucket.objects["#{path}/#{id}"].write(
35
- data,
36
- {
37
- :content_type => content_type,
38
- :metadata => meta
31
+ objects = client.list_objects(
32
+ bucket: bucket,
33
+ prefix: path
34
+ )
35
+
36
+ client.delete_objects(
37
+ bucket: bucket,
38
+ delete: {
39
+ objects: objects.data.contents.map { |o| { key: o.key } }
39
40
  }
40
41
  )
42
+ end
41
43
 
44
+ def put(key, data, content_type = "text/plain", meta = {})
42
45
  logger.metric "StoragePuts"
43
46
  logger.info(
44
- "event" => "StorageObjectStored",
45
- "path" => path,
46
- "id" => id,
47
- "method" => "#{self.class}#put"
47
+ "event" => "StorageObjectStored",
48
+ "bucket" => bucket,
49
+ "path" => path,
50
+ "key" => key,
51
+ "method" => "#{self.class}#put"
52
+ )
53
+
54
+ client.put_object(
55
+ bucket: bucket,
56
+ body: data,
57
+ key: [path, key].join('/'),
58
+ content_type: content_type,
59
+ metadata: meta
48
60
  )
49
61
  end
50
62
 
51
- def get(id)
52
- object = bucket.objects["#{path}/#{id}"]
53
- content = object.read
54
- content_type = object.content_type
55
- meta_data = object.metadata.to_h.merge(add_custom_meta(object))
63
+ def get(key)
64
+ object = client.get_object(
65
+ bucket: bucket,
66
+ key: [path, key].join('/')
67
+ )
68
+
69
+ meta = object.metadata.merge(add_custom_meta(object))
56
70
 
57
71
  logger.metric "StorageGets"
58
72
  logger.info(
59
73
  "event" => "StorageObjectRetrieved",
74
+ "bucket" => bucket,
60
75
  "path" => path,
61
- "id" => id,
62
- "contentType" => content_type,
63
- "metadata" => meta_data,
76
+ "key" => key,
77
+ "contentType" => object.content_type,
78
+ "metadata" => meta,
64
79
  "method" => "#{self.class}#get"
65
80
  )
66
81
 
67
82
  {
68
- :content => content,
69
- :content_type => content_type,
70
- :meta => meta_data
83
+ :content => object.body.read,
84
+ :content_type => object.content_type,
85
+ :meta => Hash[meta.map { |k, v| [k.to_sym, v] }]
71
86
  }
72
87
  end
73
88
 
@@ -79,5 +94,11 @@ module Alephant
79
94
  :"head_Last-Modified" => DateTime.parse(object.last_modified.to_s).httpdate
80
95
  }
81
96
  end
97
+
98
+ def client
99
+ options = {}
100
+ options[:endpoint] = ENV['AWS_S3_ENDPOINT'] if ENV['AWS_S3_ENDPOINT']
101
+ @client ||= ::Aws::S3::Client.new(options)
102
+ end
82
103
  end
83
104
  end
@@ -1,5 +1,5 @@
1
1
  module Alephant
2
2
  class Storage
3
- VERSION = "1.1.1"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -2,100 +2,128 @@ require "spec_helper"
2
2
  require "date"
3
3
 
4
4
  describe Alephant::Storage do
5
- let(:id) { :id }
5
+ let(:bucket) { 'my-bucket' }
6
+ let(:key) { 'my-key' }
6
7
  let(:path) { :path }
7
- let(:data) { :data }
8
- subject { Alephant::Storage }
9
-
10
- describe "initialize(id, path)" do
11
- it "sets and exposes id, path instance variables " do
12
- instance = subject.new(id, path)
13
- expect(instance.id).to eq(id)
14
- expect(instance.path).to eq(path)
15
- end
8
+ let(:data) { 'data' }
9
+ let(:fake_client) { Aws::S3::Client.new(stub_responses: true) }
10
+ subject { Alephant::Storage.new(bucket, path) }
16
11
 
17
- it "sets bucket instance variable as S3 bucket with id" do
18
- instance = subject.new(id, path)
12
+ before do
13
+ allow(subject).to receive(:client).and_return(fake_client)
14
+ end
19
15
 
20
- expect(instance.bucket).to be_an AWS::S3::Bucket
21
- expect(instance.bucket.name).to eq('id')
16
+ describe "#initialize" do
17
+ it "sets and exposes bucket, path instance variables " do
18
+ expect(subject.bucket).to eq(bucket)
19
+ expect(subject.path).to eq(path)
22
20
  end
23
21
  end
24
22
 
25
- describe "clear" do
26
- let(:num_object_in_path) { 100 }
27
- it "deletes all objects for a path" do
28
- filtered_object_collection = double()
29
- expect(filtered_object_collection)
30
- .to receive(:delete_all)
23
+ describe "#clear" do
24
+ before do
25
+ fake_client.stub_responses(:list_objects, contents: fake_objects)
26
+ end
31
27
 
32
- s3_object_collection = double()
33
- expect(s3_object_collection)
34
- .to receive(:with_prefix)
35
- .with(path)
36
- .and_return(filtered_object_collection)
28
+ context 'with objects' do
29
+ let(:fake_objects) { [ { key: key } ] }
37
30
 
38
- s3_bucket = double()
39
- expect(s3_bucket).to receive(:objects).and_return(s3_object_collection)
31
+ it 'deletes all objects for a path' do
32
+ expect(subject.clear.data).to be_a(Aws::S3::Types::DeleteObjectsOutput)
33
+ end
34
+ end
40
35
 
41
- expect_any_instance_of(AWS::S3).to receive(:buckets).and_return({ id => s3_bucket })
36
+ context 'with no objects' do
37
+ let(:fake_objects) { [] }
42
38
 
43
- instance = subject.new(id, path)
44
- instance.clear
39
+ it 'returns DeleteObjectsOutput response' do
40
+ expect(subject.clear.data).to be_a(Aws::S3::Types::DeleteObjectsOutput)
41
+ end
45
42
  end
46
43
  end
47
44
 
48
- describe "put(id, data)" do
49
- it "sets bucket path/id content data" do
50
- s3_object_collection = double()
51
- expect(s3_object_collection).to receive(:write).with(:data, { :content_type => 'foo/bar', :metadata=>{} })
45
+ describe "#put" do
46
+ it "sets bucket path/key content data" do
47
+ expect(subject.put(key, data, 'foo/bar').data).to be_a(Aws::S3::Types::PutObjectOutput)
48
+ end
49
+ end
52
50
 
53
- s3_bucket = double()
54
- expect(s3_bucket).to receive(:objects).and_return(
55
- {
56
- "path/id" => s3_object_collection
57
- }
58
- )
51
+ describe "#get" do
52
+ context 'with object response' do
53
+ before do
54
+ fake_client.stub_responses(:get_object, {
55
+ body: 'content',
56
+ etag: 'foo_123',
57
+ metadata: meta,
58
+ content_type: content_type,
59
+ last_modified: Time.parse(last_modified)
60
+ })
61
+ end
62
+
63
+ context 'with meta' do
64
+ let(:meta) do
65
+ {
66
+ 'foo' => 'bar'
67
+ }
68
+ end
69
+
70
+ let(:content_type) { 'foo/bar' }
71
+ let(:last_modified) { '2016-04-11 10:39:57 +0000' }
72
+
73
+ it "gets bucket path/key content data" do
74
+ expected_hash = {
75
+ :content => "content",
76
+ :content_type => "foo/bar",
77
+ :meta => {
78
+ :foo => 'bar',
79
+ :head_ETag => "foo_123",
80
+ :"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
81
+ }
82
+ }
83
+
84
+ expect(subject.get(key)).to eq(expected_hash)
85
+ end
86
+ end
87
+
88
+ context 'with no meta' do
89
+ let(:meta) { {} }
90
+
91
+ let(:content_type) { 'foo/bar' }
92
+ let(:last_modified) { '2016-04-11 10:39:57 +0000' }
93
+
94
+ it "gets bucket path/key content data" do
95
+ expected_hash = {
96
+ :content => "content",
97
+ :content_type => "foo/bar",
98
+ :meta => {
99
+ :head_ETag => "foo_123",
100
+ :"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
101
+ }
102
+ }
103
+
104
+ expect(subject.get(key)).to eq(expected_hash)
105
+ end
106
+ end
107
+ end
59
108
 
60
- expect_any_instance_of(AWS::S3).to receive(:buckets).and_return({ id => s3_bucket })
61
- instance = subject.new(id, path)
109
+ context 'object does not exist' do
110
+ before do
111
+ fake_client.stub_responses(:get_object, 'NoSuchKey')
112
+ end
62
113
 
63
- instance.put(id, data, "foo/bar")
114
+ it 'raises NoSuchKey' do
115
+ expect { subject.get(key) }.to raise_error(Aws::S3::Errors::NoSuchKey)
116
+ end
64
117
  end
65
- end
66
118
 
67
- describe "get(id)" do
68
- it "gets bucket path/id content data" do
69
- s3_object_collection = double()
70
- expect(s3_object_collection).to receive(:read).and_return("content")
71
- expect(s3_object_collection).to receive(:content_type).and_return("foo/bar" )
72
- expect(s3_object_collection).to receive(:metadata).and_return({ :foo => :bar })
73
- expect(s3_object_collection).to receive(:etag).and_return("foo_123")
74
- expect(s3_object_collection).to receive(:last_modified).and_return(DateTime.parse("2016-04-11 10:39:57 +0000"))
75
-
76
- s3_bucket = double()
77
- expect(s3_bucket).to receive(:objects).and_return(
78
- {
79
- "path/id" => s3_object_collection
80
- }
81
- )
82
-
83
- expect_any_instance_of(AWS::S3).to receive(:buckets).and_return({ id => s3_bucket })
84
-
85
- instance = subject.new(id, path)
86
- object_hash = instance.get(id)
87
-
88
- expected_hash = {
89
- :content => "content",
90
- :content_type => "foo/bar",
91
- :meta => {
92
- :foo => :bar,
93
- :head_ETag => "foo_123",
94
- :"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
95
- }
96
- }
97
-
98
- expect(object_hash).to eq(expected_hash)
119
+ context 'bucket does not exist' do
120
+ before do
121
+ fake_client.stub_responses(:get_object, 'NoSuchBucket')
122
+ end
123
+
124
+ it 'raises NoSuchBucket' do
125
+ expect { subject.get(key) }.to raise_error(Aws::S3::Errors::NoSuchBucket)
126
+ end
99
127
  end
100
128
  end
101
129
  end
metadata CHANGED
@@ -1,185 +1,185 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alephant-storage
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BBC News
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-14 00:00:00.000000000 Z
11
+ date: 2018-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: rspec
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
- - - '>='
17
+ - - ">="
17
18
  - !ruby/object:Gem::Version
18
19
  version: '0'
19
- name: rspec
20
- prerelease: false
21
20
  type: :development
21
+ prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
+ name: rspec-nc
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - '>='
31
+ - - ">="
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
- name: rspec-nc
34
- prerelease: false
35
34
  type: :development
35
+ prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
+ name: guard
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
- - - '>='
45
+ - - ">="
45
46
  - !ruby/object:Gem::Version
46
47
  version: '0'
47
- name: guard
48
- prerelease: false
49
48
  type: :development
49
+ prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
+ name: guard-rspec
56
57
  requirement: !ruby/object:Gem::Requirement
57
58
  requirements:
58
- - - '>='
59
+ - - ">="
59
60
  - !ruby/object:Gem::Version
60
61
  version: '0'
61
- name: guard-rspec
62
- prerelease: false
63
62
  type: :development
63
+ prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
+ name: pry
70
71
  requirement: !ruby/object:Gem::Requirement
71
72
  requirements:
72
- - - '>='
73
+ - - ">="
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
- name: pry
76
- prerelease: false
77
76
  type: :development
77
+ prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
+ name: pry-remote
84
85
  requirement: !ruby/object:Gem::Requirement
85
86
  requirements:
86
- - - '>='
87
+ - - ">="
87
88
  - !ruby/object:Gem::Version
88
89
  version: '0'
89
- name: pry-remote
90
- prerelease: false
91
90
  type: :development
91
+ prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
+ name: pry-nav
98
99
  requirement: !ruby/object:Gem::Requirement
99
100
  requirements:
100
- - - '>='
101
+ - - ">="
101
102
  - !ruby/object:Gem::Version
102
103
  version: '0'
103
- name: pry-nav
104
- prerelease: false
105
104
  type: :development
105
+ prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
+ name: bundler
112
113
  requirement: !ruby/object:Gem::Requirement
113
114
  requirements:
114
- - - ~>
115
+ - - "~>"
115
116
  - !ruby/object:Gem::Version
116
117
  version: '1.5'
117
- name: bundler
118
- prerelease: false
119
118
  type: :development
119
+ prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ~>
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '1.5'
125
125
  - !ruby/object:Gem::Dependency
126
+ name: rake
126
127
  requirement: !ruby/object:Gem::Requirement
127
128
  requirements:
128
- - - '>='
129
+ - - ">="
129
130
  - !ruby/object:Gem::Version
130
131
  version: '0'
131
- name: rake
132
- prerelease: false
133
132
  type: :development
133
+ prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - '>='
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
+ name: aws-sdk-s3
140
141
  requirement: !ruby/object:Gem::Requirement
141
142
  requirements:
142
- - - ~>
143
+ - - ">="
143
144
  - !ruby/object:Gem::Version
144
- version: '1.0'
145
- name: aws-sdk
146
- prerelease: false
145
+ version: '0'
147
146
  type: :runtime
147
+ prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ~>
150
+ - - ">="
151
151
  - !ruby/object:Gem::Version
152
- version: '1.0'
152
+ version: '0'
153
153
  - !ruby/object:Gem::Dependency
154
+ name: alephant-logger
154
155
  requirement: !ruby/object:Gem::Requirement
155
156
  requirements:
156
- - - '>='
157
+ - - ">="
157
158
  - !ruby/object:Gem::Version
158
159
  version: '0'
159
- name: alephant-logger
160
- prerelease: false
161
160
  type: :runtime
161
+ prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - '>='
164
+ - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- description:
167
+ description:
168
168
  email:
169
- - FutureMediaNewsRubyGems@bbc.co.uk
169
+ - D&ENewsFrameworksTeam@bbc.co.uk
170
170
  executables: []
171
171
  extensions: []
172
172
  extra_rdoc_files: []
173
173
  files:
174
- - .gitignore
175
- - .ruby-version
176
- - .travis.yml
174
+ - ".gitignore"
175
+ - ".ruby-version"
176
+ - ".travis.yml"
177
177
  - Gemfile
178
178
  - Guardfile
179
179
  - LICENSE.txt
180
180
  - README.md
181
181
  - Rakefile
182
- - alephant-cache.gemspec
182
+ - alephant-storage.gemspec
183
183
  - lib/alephant/storage.rb
184
184
  - lib/alephant/storage/version.rb
185
185
  - spec/spec_helper.rb
@@ -188,24 +188,24 @@ homepage: https://github.com/BBC-News/alephant-storage
188
188
  licenses:
189
189
  - MIT
190
190
  metadata: {}
191
- post_install_message:
191
+ post_install_message:
192
192
  rdoc_options: []
193
193
  require_paths:
194
194
  - lib
195
195
  required_ruby_version: !ruby/object:Gem::Requirement
196
196
  requirements:
197
- - - '>='
197
+ - - ">="
198
198
  - !ruby/object:Gem::Version
199
199
  version: '0'
200
200
  required_rubygems_version: !ruby/object:Gem::Requirement
201
201
  requirements:
202
- - - '>='
202
+ - - ">="
203
203
  - !ruby/object:Gem::Version
204
204
  version: '0'
205
205
  requirements: []
206
- rubyforge_project:
207
- rubygems_version: 2.1.9
208
- signing_key:
206
+ rubyforge_project:
207
+ rubygems_version: 2.6.12
208
+ signing_key:
209
209
  specification_version: 4
210
210
  summary: Simple abstraction layer over S3 for get/put.
211
211
  test_files: