duracloud-client 0.1.5 → 0.2.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: f562bbe280990009391a82bc2480a8b829622eff
4
- data.tar.gz: 25b2dec1653acd45e707a21b0b2d80c27728baaf
3
+ metadata.gz: 45e670a025414a048dd0e4043e25a370ef6085ee
4
+ data.tar.gz: 43ec5144e26fa7fb3334652bece16f293ddf3324
5
5
  SHA512:
6
- metadata.gz: 742c9b3da2bb773f25f0cd4521981cb65fa1e620f74a8028ff31ddafc5d3eb0359e1a4ed2450fc718b7d25d9c2de2da6d57282d77e22b750306f07862eda183d
7
- data.tar.gz: d909359cb467c461d89854eca31cc5c116db8c6d782ea5fed0daf13db8a1a540fbddf6ded1fc550772cd7420cec62d92685255c8b81cf0c949ab59960d1f229e
6
+ metadata.gz: cc638f78a134b5be821ceab5269ed84abe98d8f09bfa3d4122c1bf878dd1ab43d846fe8caf75c933e75fc868c9f3b80df8c7f98314702296535c7d6d10201c33
7
+ data.tar.gz: 84dbd31e44ebbc41c3f62ecbc2d9e2c1164f3ef5823818ebb88e4ec9c9051d4d6d15b6334210bf4a226f19851a1e40c3b2186dcd64a5ad568e53f2b19701a4ad
data/.travis.yml CHANGED
@@ -1,8 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1
4
3
  - 2.2
5
- - 2.3.0
4
+ - 2.3.1
6
5
  gemfile:
7
- - gemfiles/Gemfile.activemodel-4.1
8
6
  - gemfiles/Gemfile.activemodel-4.2
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
+ ruby '2.3.1'
2
3
 
3
4
  # Specify your gem's dependencies in duracloud.gemspec
4
5
  gemspec
6
+
7
+ gem 'activemodel', '~> 4.2.7'
data/README.md CHANGED
@@ -6,7 +6,7 @@ Ruby client for communicating with DuraCloud
6
6
  Add this line to your application's Gemfile:
7
7
 
8
8
  ```ruby
9
- gem 'duracloud'
9
+ gem 'duracloud-client'
10
10
  ```
11
11
 
12
12
  And then execute:
@@ -15,7 +15,7 @@ And then execute:
15
15
 
16
16
  Or install it yourself as:
17
17
 
18
- $ gem install duracloud
18
+ $ gem install duracloud-client
19
19
 
20
20
  ## Usage
21
21
 
@@ -125,7 +125,7 @@ foo8
125
125
  #### Create a new content item and store it in DuraCloud
126
126
 
127
127
  ```
128
- >> new_content = Duracloud::Content.new("rest-api-testing", "ark:/99999/fk4zzzz")
128
+ >> new_content = Duracloud::Content.new(space_id: "rest-api-testing", content_id: "ark:/99999/fk4zzzz")
129
129
  => #<Duracloud::Content space_id="rest-api-testing", content_id="ark:/99999/fk4zzzz", store_id=(default)>
130
130
 
131
131
  >> new_content.body = "test"
@@ -138,16 +138,20 @@ foo8
138
138
  => #<Duracloud::Content space_id="rest-api-testing", content_id="ark:/99999/fk4zzzz", store_id=(default)>
139
139
  ```
140
140
 
141
- When storing content a `Duracloud::NotFoundError` is raised if the space does not exist. A `Duracloud::BadRequestError` is raised if the content ID is invalid.
141
+ When storing content a `Duracloud::NotFoundError` is raised if the space does not exist.
142
+ A `Duracloud::BadRequestError` is raised if the content ID is invalid.
143
+ A `Duracloud::ConflictError` is raised if the provided MD5 digest does not match the stored digest.
142
144
 
143
145
  #### Retrieve an existing content item from DuraCloud
144
146
 
145
147
  ```
146
- >> Duracloud::Content.find("spaceID", "contentID")
148
+ >> Duracloud::Content.find(space_id: "spaceID", content_id: "contentID")
147
149
  => #<Duracloud::Content space_id="spaceID", content_id="contentID", store_id=(default)>
148
150
  ```
149
151
 
150
152
  If the space or content ID does not exist, a `Duracloud::NotFoundError` is raised.
153
+ If an MD5 digest is provided (:md5 attribute), a `Duracloud::MessageDigestError` is
154
+ raised if the content ID exists and the stored digest does not match.
151
155
 
152
156
  #### Update the properties for a content item
153
157
 
data/duracloud.gemspec CHANGED
@@ -18,11 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.required_ruby_version = ">= 2.1"
21
+ spec.required_ruby_version = ">= 2.2"
22
22
 
23
23
  spec.add_dependency "hashie", "~> 3.4"
24
24
  spec.add_dependency "httpclient", "~> 2.7"
25
- spec.add_dependency "activemodel", "~> 4.1"
25
+ spec.add_dependency "activemodel", ">= 4.2", "< 6"
26
26
  spec.add_dependency "nokogiri", "~> 1.6"
27
27
 
28
28
  spec.add_development_dependency "webmock", "~> 2.0"
@@ -1,3 +1,3 @@
1
1
  source 'https://rubygems.org'
2
- gem "activemodel", "~> 4.1.16"
2
+ gem "activemodel", "~> 5.0.1"
3
3
  gemspec path: "../"
@@ -1,4 +1,3 @@
1
- require "stringio"
2
1
  require "active_model"
3
2
 
4
3
  module Duracloud
@@ -6,6 +5,7 @@ module Duracloud
6
5
  # A piece of content in DuraCloud
7
6
  #
8
7
  class Content
8
+ include ActiveModel::Model
9
9
  include ActiveModel::Dirty
10
10
  include Persistence
11
11
  include HasProperties
@@ -15,39 +15,31 @@ module Duracloud
15
15
  after_save :changes_applied
16
16
 
17
17
  # Does the content exist in DuraCloud?
18
- # @see .new for arguments
19
18
  # @return [Boolean] whether the content exists
20
- def self.exist?(*args)
21
- find(*args) && true
19
+ # @raise [Duracloud::MessageDigestError] the provided digest in the :md5 attribute
20
+ # does not match the stored value
21
+ def self.exist?(params={})
22
+ find(params) && true
22
23
  rescue NotFoundError
23
24
  false
24
25
  end
25
26
 
26
27
  # Find content in DuraCloud.
27
- # @see .new for arguments
28
28
  # @return [Duraclound::Content] the content
29
29
  # @raise [Duracloud::NotFoundError] the space, content, or store does not exist.
30
- def self.find(*args)
31
- new(*args) { |content| content.load_properties }
30
+ # @raise [Duracloud::MessageDigestError] the provided digest in the :md5 attribute
31
+ # does not match the stored value
32
+ def self.find(params={})
33
+ new(params).tap do |content|
34
+ content.load_properties
35
+ end
32
36
  end
33
37
 
34
- attr_reader :space_id, :content_id, :store_id
38
+ attr_accessor :space_id, :content_id, :store_id
35
39
  alias_method :id, :content_id
40
+ validates_presence_of :space_id, :content_id
36
41
 
37
- define_attribute_methods :content_type, :body
38
-
39
- # @param space_id [String] The space ID (required)
40
- # @param content_id [String] The content ID (required)
41
- # @param store_id [String] the store ID (optional)
42
- # @example
43
- # new("myspace", "mycontent.txt")
44
- def initialize(space_id, content_id, store_id = nil)
45
- @content_id = content_id
46
- @space_id = space_id
47
- @store_id = store_id
48
- @body, @content_type = nil, nil
49
- yield self if block_given?
50
- end
42
+ define_attribute_methods :content_type, :body, :md5
51
43
 
52
44
  # Return the space associated with this content.
53
45
  # @return [Duracloud::Space] the space.
@@ -66,13 +58,15 @@ module Duracloud
66
58
  # @raise [Duracloud::NotFoundError] the content does not exist in DuraCloud.
67
59
  def load_body
68
60
  response = Client.get_content(*args, **query)
61
+ set_md5!(response)
69
62
  @body = response.body # don't use setter b/c marks as dirty
70
63
  persisted!
71
64
  end
72
65
 
73
66
  def load_properties
74
67
  super do |response|
75
- # don't mark content_type as changed
68
+ # don't mark content_type or md5 as changed
69
+ set_md5!(response)
76
70
  @content_type = response.content_type
77
71
  end
78
72
  end
@@ -104,8 +98,28 @@ module Duracloud
104
98
  @content_type
105
99
  end
106
100
 
101
+ def md5=(val)
102
+ md5_will_change! unless val == @md5
103
+ @md5 = val
104
+ end
105
+
106
+ def md5
107
+ @md5
108
+ end
109
+
107
110
  private
108
111
 
112
+ def set_md5!(response)
113
+ if md5
114
+ if md5 != response.md5
115
+ raise MessageDigestError,
116
+ "Expected MD5 digest (#{md5}) does not match response header: #{response.md5}"
117
+ end
118
+ else
119
+ @md5 = response.md5
120
+ end
121
+ end
122
+
109
123
  def io_like?
110
124
  body.respond_to?(:read) && body.respond_to?(:rewind)
111
125
  end
@@ -119,7 +133,7 @@ module Duracloud
119
133
 
120
134
  def store
121
135
  headers = {
122
- "Content-MD5" => md5,
136
+ "Content-MD5" => md5 || calculate_md5,
123
137
  "Content-Type" => content_type || "application/octet-stream"
124
138
  }
125
139
  headers.merge!(properties)
@@ -127,7 +141,7 @@ module Duracloud
127
141
  Client.store_content(*args, **options)
128
142
  end
129
143
 
130
- def md5
144
+ def calculate_md5
131
145
  digest = Digest::MD5.new
132
146
  if io_like?
133
147
  body.rewind
@@ -4,4 +4,5 @@ module Duracloud
4
4
  class NotFoundError < Error; end
5
5
  class BadRequestError < Error; end
6
6
  class ConflictError < Error; end
7
+ class MessageDigestError < Error; end
7
8
  end
@@ -25,7 +25,7 @@ module Duracloud
25
25
  INTERNAL = /\A#{PREFIX}content-(mimetype|size|checksum|modified)\z/
26
26
 
27
27
  # Properties set by the DuraCloud SyncTool
28
- SYNCTOOL = /\A#{PREFIX}(creator|(content-file-(created|modified|last-accessed-path)))\z/
28
+ SYNCTOOL = /\A#{PREFIX}(creator|(content-file-(created|modified|last-accessed|path)))\z/
29
29
 
30
30
  # Is the property valid for this class of properties?
31
31
  # @note Subclasses should override this method rather than the `#property?'
@@ -160,7 +160,7 @@ module Duracloud
160
160
  # @return [Duracloud::Content] the content item.
161
161
  # @raise [Duracloud::NotFoundError] if the content item does not exist.
162
162
  def find_content(content_id)
163
- Content.find(space_id, content_id, store_id)
163
+ Content.find(space_id: space_id, content_id: content_id, store_id: store_id)
164
164
  end
165
165
 
166
166
  # Return the audit log for the space
@@ -1,3 +1,3 @@
1
1
  module Duracloud
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -7,31 +7,64 @@ module Duracloud
7
7
  describe "when it exists" do
8
8
  before { stub_request(:head, url) }
9
9
  specify {
10
- expect(Content.find("foo", "bar")).to be_a(Content)
10
+ expect(Content.find(space_id: "foo", content_id: "bar")).to be_a(Content)
11
11
  }
12
12
  end
13
13
  describe "when it does not exist" do
14
14
  before { stub_request(:head, url).to_return(status: 404) }
15
15
  specify {
16
- expect { Content.find("foo", "bar") }.to raise_error(NotFoundError)
16
+ expect { Content.find(space_id: "foo", content_id: "bar") }.to raise_error(NotFoundError)
17
17
  }
18
18
  end
19
+ describe "when providing an MD5" do
20
+ before do
21
+ stub_request(:head, url).to_return(headers: {'Content-MD5'=>'foo'})
22
+ end
23
+ describe "that is correct" do
24
+ specify {
25
+ expect(Content.find(space_id: "foo", content_id: "bar", md5: "foo")).to be_a(Content)
26
+ }
27
+ end
28
+ describe "that is incorrect" do
29
+ specify {
30
+ expect { Content.find(space_id: "foo", content_id: "bar", md5: "bar") }.to raise_error(MessageDigestError)
31
+ }
32
+ end
33
+ end
19
34
  end
20
35
 
21
36
  describe ".exist?" do
22
- subject { Content.exist?("foo", "bar") }
23
37
  describe "when it exists" do
24
38
  before { stub_request(:head, url) }
25
- it { is_expected.to be true }
39
+ specify {
40
+ expect(Content.exist?(space_id: "foo", content_id: "bar")).to be true
41
+ }
26
42
  end
27
43
  describe "when it does not exist" do
28
44
  before { stub_request(:head, url).to_return(status: 404) }
29
- it { is_expected.to be false }
45
+ specify {
46
+ expect(Content.exist?(space_id: "foo", content_id: "bar")).to be false
47
+ }
48
+ end
49
+ describe "when providing an MD5" do
50
+ before do
51
+ stub_request(:head, url).to_return(headers: {'Content-MD5'=>'foo'})
52
+ end
53
+ describe "that is correct" do
54
+ specify {
55
+ expect(Content.exist?(space_id: "foo", content_id: "bar", md5: "foo")).to be true
56
+ }
57
+ end
58
+ describe "that is incorrect" do
59
+ specify {
60
+ expect { Content.exist?(space_id: "foo", content_id: "bar", md5: "bar") }.to raise_error(MessageDigestError)
61
+ }
62
+ end
30
63
  end
31
64
  end
32
65
 
33
66
  describe "#save" do
34
- subject { Content.new("foo", "bar") }
67
+ subject { Content.new(space_id: "foo", content_id: "bar") }
35
68
  describe "when not persisted" do
36
69
  describe "when empty" do
37
70
  it "raises an exception" do
@@ -51,7 +84,9 @@ module Duracloud
51
84
  end
52
85
  describe "and the space exists" do
53
86
  before {
54
- stub_request(:put, url).with(body: "Some file content")
87
+ stub_request(:put, url)
88
+ .with(body: "Some file content",
89
+ headers: {"Content-MD5"=>"92bbcf620ceb5f5bf38f08e9a1f31e7b"})
55
90
  .to_return(status: 201)
56
91
  }
57
92
  it "stores the content" do
@@ -68,7 +103,9 @@ module Duracloud
68
103
  }
69
104
  describe "and the body has changed" do
70
105
  before {
71
- stub_request(:put, url).with(body: "Some file content")
106
+ stub_request(:put, url)
107
+ .with(body: "Some file content",
108
+ headers: {"Content-MD5"=>"92bbcf620ceb5f5bf38f08e9a1f31e7b"})
72
109
  .to_return(status: 201)
73
110
  }
74
111
  it "stores the content" do
@@ -91,10 +128,12 @@ module Duracloud
91
128
  describe "when the body is a file" do
92
129
  let(:path) { File.expand_path('../../fixtures/lorem_ipsum.txt', __FILE__) }
93
130
  let(:file) { File.new(path, "rb") }
94
- before {
95
- stub_request(:put, url).with(body: File.read(path))
131
+ before do
132
+ stub_request(:put, url)
133
+ .with(body: File.read(path),
134
+ headers: {"Content-MD5"=>"039d7100bea9ef2efbe151db953726ce"})
96
135
  .to_return(status: 201)
97
- }
136
+ end
98
137
  it "stores the file content" do
99
138
  subject.body = file
100
139
  subject.save
@@ -103,7 +142,7 @@ module Duracloud
103
142
  end
104
143
 
105
144
  describe "#delete" do
106
- subject { Content.new("foo", "bar") }
145
+ subject { Content.new(space_id: "foo", content_id: "bar") }
107
146
  describe "when not found" do
108
147
  before { stub_request(:delete, url).to_return(status: 404) }
109
148
  it "raises an exception" do
@@ -124,11 +163,12 @@ module Duracloud
124
163
  allow(Client).to receive(:get_content_properties)
125
164
  .with("foo", "bar", hash_including(storeID: nil)) {
126
165
  double(headers: {'x-dura-meta-creator'=>'testuser'},
127
- content_type: 'text/plain')
166
+ content_type: 'text/plain',
167
+ md5: '08a008a01d498c404b0c30852b39d3b8')
128
168
  }
129
169
  }
130
170
  specify {
131
- content = Content.find("foo", "bar")
171
+ content = Content.find(space_id: "foo", content_id: "bar")
132
172
  expect(content.properties.x_dura_meta_creator).to eq('testuser')
133
173
  }
134
174
  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.1.5
4
+ version: 0.2.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: 2016-09-30 00:00:00.000000000 Z
11
+ date: 2017-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -42,16 +42,22 @@ dependencies:
42
42
  name: activemodel
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.2'
48
+ - - "<"
46
49
  - !ruby/object:Gem::Version
47
- version: '4.1'
50
+ version: '6'
48
51
  type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
- - - "~>"
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '4.2'
58
+ - - "<"
53
59
  - !ruby/object:Gem::Version
54
- version: '4.1'
60
+ version: '6'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: nokogiri
57
63
  requirement: !ruby/object:Gem::Requirement
@@ -151,8 +157,8 @@ files:
151
157
  - README.md
152
158
  - Rakefile
153
159
  - duracloud.gemspec
154
- - gemfiles/Gemfile.activemodel-4.1
155
160
  - gemfiles/Gemfile.activemodel-4.2
161
+ - gemfiles/Gemfile.activemodel-5.0
156
162
  - lib/duracloud-client.rb
157
163
  - lib/duracloud.rb
158
164
  - lib/duracloud/audit_log.rb
@@ -204,7 +210,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
204
210
  requirements:
205
211
  - - ">="
206
212
  - !ruby/object:Gem::Version
207
- version: '2.1'
213
+ version: '2.2'
208
214
  required_rubygems_version: !ruby/object:Gem::Requirement
209
215
  requirements:
210
216
  - - ">="
@@ -212,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
218
  version: '0'
213
219
  requirements: []
214
220
  rubyforge_project:
215
- rubygems_version: 2.5.1
221
+ rubygems_version: 2.6.8
216
222
  signing_key:
217
223
  specification_version: 4
218
224
  summary: Ruby client for communicating with DuraCloud