alephant-storage 1.1.1 → 2.0.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 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: