duracloud-client 0.2.0 → 0.3.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: 45e670a025414a048dd0e4043e25a370ef6085ee
4
- data.tar.gz: 43ec5144e26fa7fb3334652bece16f293ddf3324
3
+ metadata.gz: 328f7556852dc8ca4dbdf2fed3314aa6ca2c160b
4
+ data.tar.gz: d10ae8810f1e964d495b8b88142246d42fc2c410
5
5
  SHA512:
6
- metadata.gz: cc638f78a134b5be821ceab5269ed84abe98d8f09bfa3d4122c1bf878dd1ab43d846fe8caf75c933e75fc868c9f3b80df8c7f98314702296535c7d6d10201c33
7
- data.tar.gz: 84dbd31e44ebbc41c3f62ecbc2d9e2c1164f3ef5823818ebb88e4ec9c9051d4d6d15b6334210bf4a226f19851a1e40c3b2186dcd64a5ad568e53f2b19701a4ad
6
+ metadata.gz: efa0688d30ec628915d6497422377c9edf95c06233af784947103ab110a4ea8925b7ff99854ccf829e024414eba6b370edd409dc525ff81b14c8707c1f6ccd93
7
+ data.tar.gz: 08bfcbd0b87934908c9b229b09400648699f90dd32e903b77d362033b8dcdbb1c737e3727e095b4a1343b32930dac68066895e5b5edc84f3d4e04c6e6a2db8e5
@@ -11,11 +11,7 @@ module Duracloud
11
11
  end
12
12
 
13
13
  def tsv
14
- response.body
15
- end
16
-
17
- def to_s
18
- tsv
14
+ super || response.body
19
15
  end
20
16
 
21
17
  private
@@ -19,7 +19,7 @@ module Duracloud
19
19
  end
20
20
 
21
21
  def tsv
22
- report.body
22
+ super || report.body
23
23
  end
24
24
 
25
25
  def completion_date
@@ -107,6 +107,24 @@ module Duracloud
107
107
  @md5
108
108
  end
109
109
 
110
+ # @return [Duracloud::Content] the copied content
111
+ # The current instance still represents the original content.
112
+ def copy(target_space_id:, target_content_id:, target_store_id: nil)
113
+ copy_headers = {'x-dura-meta-copy-source'=>[space_id, content_id].join('/')}
114
+ copy_headers['x-dura-meta-copy-source-store'] = store_id if store_id
115
+ options = { storeID: target_store_id, headers: copy_headers }
116
+ Client.copy_content(target_space_id, target_content_id, **options)
117
+ Content.find(space_id: target_space_id, content_id: target_content_id, store_id: target_store_id, md5: md5)
118
+ end
119
+
120
+ # @return [Duracloud::Content] the moved content
121
+ # The current instance still represents the deleted content.
122
+ def move(**args)
123
+ copied = copy(**args)
124
+ delete
125
+ copied
126
+ end
127
+
110
128
  private
111
129
 
112
130
  def set_md5!(response)
@@ -12,7 +12,7 @@ module Duracloud
12
12
  end
13
13
 
14
14
  def tsv
15
- tsv_response.body
15
+ super || tsv_response.body
16
16
  end
17
17
 
18
18
  def bagit
@@ -19,7 +19,7 @@ module Duracloud
19
19
  SPACE_ACLS = /\A#{PREFIX}acl-/
20
20
 
21
21
  # Copy Content headers
22
- COPY_CONTENT = /\A#{PREFIX}copy-source(-store)\z/
22
+ COPY_CONTENT = /\A#{PREFIX}copy-source(-store)?\z/
23
23
 
24
24
  # DuraCloud internal content properties
25
25
  INTERNAL = /\A#{PREFIX}content-(mimetype|size|checksum|modified)\z/
@@ -49,9 +49,8 @@ module Duracloud
49
49
  durastore_content(:put, space_id, content_id, **options)
50
50
  end
51
51
 
52
- def copy_content(space_id, content_id, **options)
53
- raise NotImplementedError,
54
- "The API method 'Copy Content' has not yet been implemented."
52
+ def copy_content(target_space_id, target_content_id, **options)
53
+ durastore_content(:put, target_space_id, target_content_id, **options)
55
54
  end
56
55
 
57
56
  def delete_content(space_id, content_id, **options)
@@ -91,7 +90,7 @@ module Duracloud
91
90
  end
92
91
 
93
92
  def durastore_content(http_method, space_id, content_id, **options)
94
- escaped_content_id = content_id.gsub(/%/, "%25")
93
+ escaped_content_id = content_id.gsub(/%/, "%25").gsub(/ /, "%20")
95
94
  url = [ space_id, escaped_content_id ].join("/")
96
95
  durastore(http_method, url, **options)
97
96
  end
data/lib/duracloud/tsv.rb CHANGED
@@ -2,31 +2,53 @@ require "csv"
2
2
 
3
3
  module Duracloud
4
4
  module TSV
5
- # @return [CSV::Table]
5
+
6
6
  def csv
7
- @csv ||= CSV::Table.new([]).tap do |csv|
8
- header_line, rows = tsv.split(/\r?\n/, 2)
9
- headers = header_line.split("\t").map { |h| h.downcase.gsub(/-/, "_") }
10
- header_row = CSV::Row.new(headers, headers, true)
11
- csv << header_row
12
- rows.split(/\r?\n/).each do |row|
13
- csv << row.split("\t")
14
- end
15
- end
7
+ @csv ||= CSV.new(tsv, csv_options)
16
8
  end
17
9
 
18
- # @return [Enumerator] rows as hashes
19
10
  def rows
20
- Enumerator.new do |e|
21
- csv.by_row!.each do |row|
22
- next if row.header_row?
23
- e << row.to_hash
24
- end
11
+ @rows ||= Enumerator.new do |e|
12
+ table.each { |row| e << row.to_hash }
25
13
  end
26
14
  end
27
15
 
16
+ def table
17
+ csv.rewind
18
+ csv.read
19
+ ensure
20
+ csv.rewind
21
+ end
22
+
28
23
  def tsv
29
- raise NotImplementedError, "Including module must implement `tsv`."
24
+ @tsv
25
+ end
26
+
27
+ def load_tsv(io_or_str)
28
+ @tsv = io_or_str
29
+ end
30
+
31
+ def load_tsv_file(path)
32
+ load_tsv(File.new(path, "rb"))
30
33
  end
34
+
35
+ def to_s
36
+ tsv.to_s
37
+ end
38
+
39
+ private
40
+
41
+ def csv_options
42
+ { col_sep: "\t",
43
+ quote_char: "`",
44
+ headers: true,
45
+ header_converters: header_converters,
46
+ }
47
+ end
48
+
49
+ def header_converters
50
+ lambda { |h| h.downcase.gsub(/-/, "_") }
51
+ end
52
+
31
53
  end
32
54
  end
@@ -1,3 +1,3 @@
1
1
  module Duracloud
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,22 +1,35 @@
1
1
  module Duracloud
2
2
  RSpec.describe AuditLog do
3
3
 
4
- let(:tsv) { File.read(File.expand_path('../../fixtures/audit_log.tsv', __FILE__)) }
5
-
6
- before {
7
- allow(subject).to receive(:tsv) { tsv }
8
- }
4
+ let(:path) { File.expand_path('../../fixtures/audit_log.tsv', __FILE__) }
9
5
 
10
6
  subject { described_class.new("myspace") }
11
7
 
12
- describe "CSV" do
8
+ describe "#csv" do
9
+ before {
10
+ allow(subject).to receive(:tsv) { File.read(path) }
11
+ subject.csv.read
12
+ }
13
13
  specify {
14
14
  expect(subject.csv.headers).to eq(%w(account store_id space_id content_id content_md5 content_size content_mimetype content_properties space_acls source_space_id source_content_id timestamp action username))
15
- expect(subject.csv.size).to eq(7)
16
- expect(subject.csv.to_s.split("\n").first).to eq("account,store_id,space_id,content_id,content_md5,content_size,content_mimetype,content_properties,space_acls,source_space_id,source_content_id,timestamp,action,username")
17
- expect(subject.rows.next).to eq({"account"=>"example", "store_id"=>"1065", "space_id"=>"myspace", "content_id"=>"", "content_md5"=>"", "content_size"=>"", "content_mimetype"=>"", "content_properties"=>"", "space_acls"=>"", "source_space_id"=>"", "source_content_id"=>"", "timestamp"=>"2016-04-27T18:34:18.018", "action"=>"CREATE_SPACE", "username"=>"bob@example.com"})
15
+ expect(subject.rows.to_a.size).to eq(6)
16
+ expect(subject.rows.first).to eq({"account"=>"example", "store_id"=>"1065", "space_id"=>"myspace", "content_id"=>nil, "content_md5"=>nil, "content_size"=>nil, "content_mimetype"=>nil, "content_properties"=>nil, "space_acls"=>nil, "source_space_id"=>nil, "source_content_id"=>nil, "timestamp"=>"2016-04-27T18:34:18.018", "action"=>"CREATE_SPACE", "username"=>"bob@example.com"})
18
17
  }
19
18
  end
20
19
 
20
+ describe "#load_tsv" do
21
+ it "loads a string" do
22
+ tsv = File.read(path)
23
+ subject.load_tsv(tsv)
24
+ expect(subject.tsv).to eq(tsv)
25
+ end
26
+ it "loads an IO" do
27
+ tsv = File.read(path)
28
+ tsv_io = File.new(path, "rb")
29
+ subject.load_tsv(tsv)
30
+ expect(subject.tsv.to_s).to eq(tsv)
31
+ end
32
+ end
33
+
21
34
  end
22
35
  end
@@ -1,22 +1,35 @@
1
1
  module Duracloud
2
2
  RSpec.describe BitIntegrityReport do
3
3
 
4
- let(:tsv) { File.read(File.expand_path('../../fixtures/bit_integrity_report.tsv', __FILE__)) }
5
-
6
- before {
7
- allow(subject).to receive(:tsv) { tsv }
8
- }
4
+ let(:path) { File.expand_path('../../fixtures/bit_integrity_report.tsv', __FILE__) }
9
5
 
10
6
  subject { described_class.new("myspace") }
11
7
 
12
- describe "CSV" do
8
+ describe "#csv" do
9
+ before do
10
+ allow(subject).to receive(:tsv) { File.read(path) }
11
+ subject.csv.read
12
+ end
13
13
  specify {
14
14
  expect(subject.csv.headers).to eq(%w(date_checked account store_id store_type space_id content_id result content_checksum provider_checksum manifest_checksum details))
15
- expect(subject.csv.size).to eq(4)
16
- expect(subject.csv.to_s.split("\n").first).to eq("date_checked,account,store_id,store_type,space_id,content_id,result,content_checksum,provider_checksum,manifest_checksum,details")
17
- expect(subject.rows.next).to eq({"date_checked"=>"2016-05-15T04:11:14", "account"=>"example", "store_id"=>"1065", "store_type"=>"AMAZON_S3", "space_id"=>"myspace", "content_id"=>"BINARIES/00/00/e8/0000e819ac3e67d039d288adaab5b5e44c3c21d9", "result"=>"SUCCESS", "content_checksum"=>"27333f3c06a6d259863384799be68d30", "provider_checksum"=>"27333f3c06a6d259863384799be68d30", "manifest_checksum"=>"27333f3c06a6d259863384799be68d30", "details"=>"--"})
15
+ expect(subject.rows.to_a.size).to eq(3)
16
+ expect(subject.rows.first).to eq({"date_checked"=>"2016-05-15T04:11:14", "account"=>"example", "store_id"=>"1065", "store_type"=>"AMAZON_S3", "space_id"=>"myspace", "content_id"=>"BINARIES/00/00/e8/0000e819ac3e67d039d288adaab5b5e44c3c21d9", "result"=>"SUCCESS", "content_checksum"=>"27333f3c06a6d259863384799be68d30", "provider_checksum"=>"27333f3c06a6d259863384799be68d30", "manifest_checksum"=>"27333f3c06a6d259863384799be68d30", "details"=>"--"})
18
17
  }
19
18
  end
20
19
 
20
+ describe "#load_tsv" do
21
+ it "loads a string" do
22
+ tsv = File.read(path)
23
+ subject.load_tsv(tsv)
24
+ expect(subject.tsv).to eq(tsv)
25
+ end
26
+ it "loads an IO" do
27
+ tsv = File.read(path)
28
+ tsv_io = File.new(path, "rb")
29
+ subject.load_tsv(tsv)
30
+ expect(subject.tsv.to_s).to eq(tsv)
31
+ end
32
+ end
33
+
21
34
  end
22
35
  end
@@ -130,9 +130,9 @@ module Duracloud
130
130
  subject.get_content_properties("foo", "bar")
131
131
  expect(stub).to have_been_requested
132
132
  }
133
- it "escapes percent signs in the content id" do
134
- stub = stub_request(:head, "https://example.com/durastore/foo/z/z/bar%252Fbaz")
135
- subject.get_content_properties("foo", "z/z/bar%2Fbaz")
133
+ it "escapes percent signs and spaces in the content id" do
134
+ stub = stub_request(:head, "https://example.com/durastore/foo/z/z/bar%252Fbaz%20spam%20eggs")
135
+ subject.get_content_properties("foo", "z/z/bar%2Fbaz spam eggs")
136
136
  expect(stub).to have_been_requested
137
137
  end
138
138
  specify {
@@ -240,8 +240,10 @@ module Duracloud
240
240
 
241
241
  describe "copy_content" do
242
242
  specify {
243
- expect { subject.copy_content("foo", "bar", headers: {'x-dura-meta-copy-source'=>'space-id/content-id'}) }
244
- .to raise_error(NotImplementedError)
243
+ stub = stub_request(:put, "https://example.com/durastore/spam/eggs")
244
+ .with(headers: {'x-dura-meta-copy-source'=>'foo/bar'})
245
+ subject.copy_content("spam", "eggs", headers: {'x-dura-meta-copy-source'=>'foo/bar'})
246
+ expect(stub).to have_been_requested
245
247
  }
246
248
  end
247
249
 
@@ -159,19 +159,48 @@ module Duracloud
159
159
  end
160
160
 
161
161
  describe "#properties" do
162
- before {
163
- allow(Client).to receive(:get_content_properties)
164
- .with("foo", "bar", hash_including(storeID: nil)) {
165
- double(headers: {'x-dura-meta-creator'=>'testuser'},
166
- content_type: 'text/plain',
167
- md5: '08a008a01d498c404b0c30852b39d3b8')
168
- }
169
- }
162
+ before do
163
+ stub_request(:head, url)
164
+ .to_return(headers: {'x-dura-meta-creator'=>'testuser',
165
+ 'Content-Type'=>'text/plain',
166
+ 'Content-MD5'=>'08a008a01d498c404b0c30852b39d3b8'})
167
+ end
170
168
  specify {
169
+ pending "Research Webmock problem with return headers"
171
170
  content = Content.find(space_id: "foo", content_id: "bar")
172
171
  expect(content.properties.x_dura_meta_creator).to eq('testuser')
173
172
  }
174
173
  end
175
174
 
175
+ describe "#copy" do
176
+ let(:target) { "https://example.com/durastore/spam/eggs" }
177
+ subject { Content.new(space_id: "foo", content_id: "bar") }
178
+ specify {
179
+ stub1 = stub_request(:put, target)
180
+ .with(headers: {'x-dura-meta-copy-source'=>'foo/bar'})
181
+ stub2 = stub_request(:head, target)
182
+ copied = subject.copy(target_space_id: "spam", target_content_id: "eggs")
183
+ expect(copied).to be_a(Content)
184
+ expect(stub1).to have_been_requested
185
+ expect(stub2).to have_been_requested
186
+ }
187
+ end
188
+
189
+ describe "#move" do
190
+ let(:target) { "https://example.com/durastore/spam/eggs" }
191
+ subject { Content.new(space_id: "foo", content_id: "bar") }
192
+ specify {
193
+ stub1 = stub_request(:put, target)
194
+ .with(headers: {'x-dura-meta-copy-source'=>'foo/bar'})
195
+ stub2 = stub_request(:head, target)
196
+ stub3 = stub_request(:delete, "https://example.com/durastore/foo/bar")
197
+ moved = subject.move(target_space_id: "spam", target_content_id: "eggs")
198
+ expect(moved).to be_a(Content)
199
+ expect(stub1).to have_been_requested
200
+ expect(stub2).to have_been_requested
201
+ expect(stub3).to have_been_requested
202
+ }
203
+ end
204
+
176
205
  end
177
206
  end
@@ -1,22 +1,34 @@
1
1
  module Duracloud
2
2
  RSpec.describe Manifest do
3
3
 
4
- let(:tsv) { File.read(File.expand_path('../../fixtures/manifest.tsv', __FILE__)) }
5
-
6
- before {
7
- allow(subject).to receive(:tsv) { tsv }
8
- }
9
-
10
4
  subject { described_class.new("myspace") }
5
+ let(:path) { File.expand_path('../../fixtures/manifest.tsv', __FILE__) }
11
6
 
12
- describe "CSV" do
7
+ describe "#csv" do
8
+ before do
9
+ allow(subject).to receive(:tsv) { File.read(path) }
10
+ subject.csv.read
11
+ end
13
12
  specify {
14
13
  expect(subject.csv.headers).to eq(%w(space_id content_id md5))
15
- expect(subject.csv.size).to eq(4)
16
- expect(subject.csv.to_s.split("\n").first).to eq("space_id,content_id,md5")
17
- expect(subject.rows.next).to eq({"space_id"=>"myspace", "content_id"=>"METADATA/d6/42/0c/9c/d6420c9c-82f8-4f6a-baf7-37b9be7f4c5f/20160502_172925/manifest-md5.txt", "md5"=>"21fef474787860ccfb67bdd99ddee93a"})
14
+ expect(subject.rows.to_a.size).to eq(3)
15
+ expect(subject.rows.first).to eq({"space_id"=>"myspace", "content_id"=>"METADATA/d6/42/0c/9c/d6420c9c-82f8-4f6a-baf7-37b9be7f4c5f/20160502_172925/manifest-md5.txt", "md5"=>"21fef474787860ccfb67bdd99ddee93a"})
18
16
  }
19
17
  end
20
18
 
19
+ describe "#load_tsv" do
20
+ it "loads a string" do
21
+ tsv = File.read(path)
22
+ subject.load_tsv(tsv)
23
+ expect(subject.tsv).to eq(tsv)
24
+ end
25
+ it "loads an IO" do
26
+ tsv = File.read(path)
27
+ tsv_io = File.new(path, "rb")
28
+ subject.load_tsv(tsv)
29
+ expect(subject.tsv.to_s).to eq(tsv)
30
+ end
31
+ end
32
+
21
33
  end
22
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duracloud-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chandek-Stark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-25 00:00:00.000000000 Z
11
+ date: 2017-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie