duracloud-client 0.1.5 → 0.2.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: 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