duracloud-client 0.0.1 → 0.0.2
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 +4 -4
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/README.md +59 -16
- data/Rakefile +6 -0
- data/duracloud.gemspec +7 -1
- data/lib/duracloud/client.rb +30 -26
- data/lib/duracloud/configuration.rb +13 -6
- data/lib/duracloud/connection.rb +0 -10
- data/lib/duracloud/content.rb +144 -82
- data/lib/duracloud/content_properties.rb +4 -110
- data/lib/duracloud/durastore_request.rb +9 -0
- data/lib/duracloud/error_handler.rb +0 -10
- data/lib/duracloud/has_properties.rb +45 -0
- data/lib/duracloud/persistence.rb +59 -0
- data/lib/duracloud/properties.rb +108 -0
- data/lib/duracloud/request.rb +21 -16
- data/lib/duracloud/response.rb +1 -15
- data/lib/duracloud/rest_methods.rb +89 -0
- data/lib/duracloud/space.rb +104 -0
- data/lib/duracloud/space_acls.rb +23 -0
- data/lib/duracloud/space_properties.rb +22 -0
- data/lib/duracloud/store.rb +26 -0
- data/lib/duracloud/version.rb +1 -1
- data/lib/duracloud.rb +3 -1
- data/spec/fixtures/responses/GetSpaces.xml +5 -0
- data/spec/fixtures/responses/GetStores.xml +11 -0
- data/spec/spec_helper.rb +94 -0
- data/spec/unit/client_spec.rb +9 -0
- data/spec/unit/content_spec.rb +30 -0
- data/spec/unit/properties_spec.rb +13 -0
- data/spec/unit/space_spec.rb +5 -0
- data/spec/unit/store_spec.rb +5 -0
- metadata +90 -10
- data/lib/duracloud/content_request.rb +0 -16
- data/lib/duracloud/content_response.rb +0 -11
- data/lib/duracloud/request_options.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b304d53dd688ed00817d59d286e32a5d53f88acc
|
4
|
+
data.tar.gz: 065f2f3e9f0307f83e863f8ba7b1a60b680414e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d73d05c4ef0bbd91a4928b79c2c260fdec0b755ece9a43423eac2229a56874c65e24e0e7cf8a7830dcabd02a293a0945a20a585c8bb2790493dae9a5d2be9220
|
7
|
+
data.tar.gz: 6f2edaffb35c789e94e264c4f97607129e403d6673f52dcf6a28af28719e50b82c4fe0b703555923aa417144a38962300c7e8fc11ef293cc18d8f50cbc24b13c
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -43,32 +43,71 @@ end
|
|
43
43
|
=> #<Duracloud::Client:0x007fe953a1c630 @config=#<Duracloud::Configuration host="foo.duracloud.org", port=nil, user="bob@example.com", password="******">>
|
44
44
|
```
|
45
45
|
|
46
|
-
|
46
|
+
#### Logging
|
47
47
|
|
48
|
-
|
48
|
+
By default, `Duracloud::Client` logs to `STDERR`. Use the `logger` config setting to change:
|
49
49
|
|
50
|
+
```ruby
|
51
|
+
Duracloud::Client.configure do |config|
|
52
|
+
config.logger = Rails.logger
|
53
|
+
end
|
50
54
|
```
|
51
|
-
|
52
|
-
|
53
|
-
|
55
|
+
|
56
|
+
### List Storage Providers
|
57
|
+
|
58
|
+
```
|
59
|
+
> stores = Duracloud::Store.all
|
60
|
+
=> [#<Duracloud::Store:0x007faa592e9068 @owner_id="0", @primary="0", @id="1", @provider_type="AMAZON_GLACIER">, #<Duracloud::Store:0x007faa592dbd78 @owner_id="0", @primary="1", @id="2", @provider_type="AMAZON_S3">]
|
61
|
+
|
62
|
+
> stores.first.primary?
|
63
|
+
=> false
|
64
|
+
```
|
65
|
+
|
66
|
+
### Space Methods
|
67
|
+
|
68
|
+
TODO
|
69
|
+
|
70
|
+
### Content Methods
|
71
|
+
|
72
|
+
#### Create a new content item and store it in DuraCloud
|
73
|
+
|
74
|
+
1. Initialize instance of `Duracloud::Content` and save:
|
75
|
+
|
76
|
+
```
|
77
|
+
>> new_content = Duracloud::Content.new(space_id: "rest-api-testing", id: "ark:/99999/fk4zzzz")
|
78
|
+
=> #<Duracloud::Content space_id="rest-api-testing", id="ark:/99999/fk4zzzz">
|
79
|
+
|
80
|
+
>> new_content.body = "test"
|
54
81
|
=> "test"
|
55
|
-
|
82
|
+
|
83
|
+
>> new_content.content_type = "text/plain"
|
56
84
|
=> "text/plain"
|
57
|
-
|
58
|
-
|
85
|
+
|
86
|
+
>> new_content.save
|
87
|
+
=> #<Duracloud::Content space_id="rest-api-testing", id="ark:/99999/fk4zzzz">
|
88
|
+
```
|
89
|
+
|
90
|
+
2. Create with class method `Duracloud::Content.create`:
|
91
|
+
|
92
|
+
```
|
93
|
+
>> Duracloud::Content.create(space_id: "rest-api-testing", id="ark:/99999/fk4zzzz") do |c|
|
94
|
+
c.body = "test"
|
95
|
+
c.content_type = "text/plain"
|
96
|
+
end
|
97
|
+
=> #<Duracloud::Content space_id="rest-api-testing", id="ark:/99999/fk4zzzz">
|
59
98
|
```
|
60
99
|
|
61
|
-
|
100
|
+
#### Retrieve an existing content item from DuraCloud
|
62
101
|
|
63
102
|
```ruby
|
64
|
-
Duracloud::Content.find(
|
103
|
+
Duracloud::Content.find(id: "contentID", space_id: "spaceID")
|
65
104
|
```
|
66
105
|
|
67
|
-
|
106
|
+
#### Update the properties for an item
|
68
107
|
|
69
108
|
TODO
|
70
109
|
|
71
|
-
|
110
|
+
#### Delete a content item
|
72
111
|
|
73
112
|
TODO
|
74
113
|
|
@@ -76,10 +115,14 @@ TODO
|
|
76
115
|
|
77
116
|
We endeavor to follow semantic versioning. In particular, versions < 1.0 may introduce backward-incompatible changes without notice. Use at your own risk. Version 1.0 signals a stable API.
|
78
117
|
|
79
|
-
##
|
118
|
+
## Contributing
|
80
119
|
|
81
|
-
|
120
|
+
1. Fork it ( https://github.com/[my-github-username]/duracloud-ruby-client/fork )
|
121
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
122
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
123
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
124
|
+
5. Create a new Pull Request
|
82
125
|
|
83
|
-
##
|
126
|
+
## Maintainers
|
84
127
|
|
85
|
-
|
128
|
+
* [David Chandek-Stark](https://github.com/dchandekstark) (Duke University)
|
data/Rakefile
CHANGED
data/duracloud.gemspec
CHANGED
@@ -18,9 +18,15 @@ 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"
|
22
|
+
|
21
23
|
spec.add_dependency "hashie", "~> 3.4"
|
22
24
|
spec.add_dependency "httpclient", "~> 2.7"
|
25
|
+
spec.add_dependency "activemodel", "~> 4.2"
|
26
|
+
spec.add_dependency "nokogiri", "~> 1.6"
|
23
27
|
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
29
|
+
spec.add_development_dependency "rspec-its", "~> 1.2"
|
24
30
|
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
-
spec.add_development_dependency "rake", "~>
|
31
|
+
spec.add_development_dependency "rake", "~> 11.1"
|
26
32
|
end
|
data/lib/duracloud/client.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
require "forwardable"
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
|
3
|
+
require "duracloud/configuration"
|
4
|
+
require "duracloud/connection"
|
5
|
+
require "duracloud/error_handler"
|
6
|
+
require "duracloud/rest_methods"
|
5
7
|
|
6
8
|
module Duracloud
|
7
9
|
class Client
|
8
10
|
extend Forwardable
|
11
|
+
extend RestMethods
|
12
|
+
include RestMethods
|
13
|
+
|
14
|
+
def self.execute(request_class, http_method, url, **options)
|
15
|
+
new.execute(request_class, http_method, url, **options)
|
16
|
+
end
|
9
17
|
|
10
18
|
def self.configure
|
11
19
|
yield Configuration
|
@@ -13,35 +21,31 @@ module Duracloud
|
|
13
21
|
|
14
22
|
attr_reader :config
|
15
23
|
|
16
|
-
delegate [:host, :port, :user, :password, :base_url] => :config
|
24
|
+
delegate [:host, :port, :user, :password, :base_url, :logger] => :config
|
17
25
|
|
18
26
|
def initialize(**options)
|
19
27
|
@config = Configuration.new(**options)
|
20
28
|
end
|
21
29
|
|
22
|
-
def get_content(url, **options)
|
23
|
-
execute ContentRequest, :get, url, **options
|
24
|
-
#ContentRequest.get(self, url, **options)
|
25
|
-
end
|
26
|
-
|
27
|
-
def get_content_properties(url, **options)
|
28
|
-
execute ContentRequest, :head, url, **options
|
29
|
-
end
|
30
|
-
|
31
|
-
def set_content_properties(url, **options)
|
32
|
-
execute ContentRequest, :post, url, **options
|
33
|
-
end
|
34
|
-
|
35
|
-
def store_content(url, **options)
|
36
|
-
execute ContentRequest, :put, url, **options
|
37
|
-
end
|
38
|
-
|
39
|
-
def delete_content(url, **options)
|
40
|
-
execute ContentRequest, :delete, url, **options
|
41
|
-
end
|
42
|
-
|
43
30
|
def execute(request_class, http_method, url, **options)
|
44
|
-
request_class.new(self, http_method, url, **options)
|
31
|
+
request = request_class.new(self, http_method, url, **options)
|
32
|
+
response = request.execute
|
33
|
+
handle_response(response)
|
34
|
+
response
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def handle_response(response)
|
40
|
+
logger.debug([self.class.to_s, response.request_method, response.url,
|
41
|
+
response.status, response.reason].join(' '))
|
42
|
+
if response.error?
|
43
|
+
ErrorHandler.call(response)
|
44
|
+
elsif %w(POST PUT DELETE).include?(response.request_method) &&
|
45
|
+
response.plain_text? &&
|
46
|
+
response.has_body?
|
47
|
+
logger.info(response.body)
|
48
|
+
end
|
45
49
|
end
|
46
50
|
end
|
47
51
|
end
|
@@ -1,16 +1,21 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "uri"
|
3
|
+
|
1
4
|
module Duracloud
|
2
5
|
class Configuration
|
6
|
+
|
3
7
|
class << self
|
4
|
-
attr_accessor :host, :port, :user, :password
|
8
|
+
attr_accessor :host, :port, :user, :password, :logger
|
5
9
|
end
|
6
10
|
|
7
|
-
attr_reader :host, :port, :user, :password
|
11
|
+
attr_reader :host, :port, :user, :password, :logger
|
8
12
|
|
9
|
-
def initialize(host: nil, port: nil, user: nil, password: nil)
|
13
|
+
def initialize(host: nil, port: nil, user: nil, password: nil, logger: nil)
|
10
14
|
@host = host || default(:host)
|
11
15
|
@port = port || default(:port)
|
12
16
|
@user = user || default(:user)
|
13
17
|
@password = password || default(:password)
|
18
|
+
@logger = logger || Logger.new(STDERR)
|
14
19
|
freeze
|
15
20
|
end
|
16
21
|
|
@@ -18,14 +23,16 @@ module Duracloud
|
|
18
23
|
URI::HTTPS.build(host: host, port: port)
|
19
24
|
end
|
20
25
|
|
26
|
+
def inspect
|
27
|
+
"#<#{self.class} host=#{host.inspect}, port=#{port.inspect}, user=#{user.inspect}," \
|
28
|
+
" password=\"******\", logger=#{logger.inspect}>"
|
29
|
+
end
|
30
|
+
|
21
31
|
private
|
22
32
|
|
23
33
|
def default(attr)
|
24
34
|
self.class.send(attr) || ENV["DURACLOUD_#{attr.to_s.upcase}"]
|
25
35
|
end
|
26
36
|
|
27
|
-
def inspect
|
28
|
-
"#<#{self.class} host=#{host.inspect}, port=#{port.inspect}, user=#{user.inspect}, password=\"******\">"
|
29
|
-
end
|
30
37
|
end
|
31
38
|
end
|
data/lib/duracloud/connection.rb
CHANGED
@@ -9,20 +9,10 @@ module Duracloud
|
|
9
9
|
# custom case-sensitive content property headers (x-dura-meta-*).
|
10
10
|
#
|
11
11
|
class Connection < HTTPClient
|
12
|
-
# class << self
|
13
|
-
# attr_accessor :base_path
|
14
|
-
# end
|
15
|
-
|
16
|
-
# self.base_path = '/'
|
17
|
-
|
18
12
|
def initialize(client, base_path = '/')
|
19
13
|
base_url = client.base_url + base_path
|
20
14
|
super(base_url: base_url, force_basic_auth: true)
|
21
15
|
set_auth(client.base_url, client.user, client.password)
|
22
16
|
end
|
23
17
|
end
|
24
|
-
|
25
|
-
# class DurastoreConnection < Connection
|
26
|
-
# self.base_path = '/durastore/'
|
27
|
-
# end
|
28
18
|
end
|
data/lib/duracloud/content.rb
CHANGED
@@ -1,127 +1,189 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
|
1
|
+
require "uri"
|
2
|
+
require "stringio"
|
3
|
+
require "active_model"
|
4
|
+
|
5
|
+
require "duracloud/content_properties"
|
6
|
+
require "duracloud/persistence"
|
7
|
+
require "duracloud/has_properties"
|
4
8
|
|
5
9
|
module Duracloud
|
6
10
|
#
|
7
11
|
# A piece of content in DuraCloud
|
8
12
|
#
|
9
13
|
class Content
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
include ActiveModel::Dirty
|
15
|
+
include Persistence
|
16
|
+
include HasProperties
|
17
|
+
|
18
|
+
after_save :changes_applied
|
19
|
+
|
20
|
+
# Find content in DuraCloud.
|
21
|
+
#
|
22
|
+
# @param id [String] the content ID
|
23
|
+
# @param space_id [String] the space ID.
|
24
|
+
# @return [Duraclound::Content] the content
|
25
|
+
# @raise [Duracloud::NotFoundError] the space or content ID does not exist.
|
26
|
+
def self.find(id:, space_id:)
|
27
|
+
new(id: id, space_id: space_id) do |content|
|
28
|
+
content.load_properties
|
15
29
|
end
|
30
|
+
end
|
16
31
|
|
17
|
-
|
18
|
-
|
32
|
+
# Store content in DuraCloud
|
33
|
+
#
|
34
|
+
# @param id [String] The content ID
|
35
|
+
# @param space_id [String] The space ID.
|
36
|
+
# @param body [String, #read] The content body
|
37
|
+
# @return [Duracloud::Content] the content
|
38
|
+
# @raise [Duracloud::NotFoundError] if the space ID does not exist
|
39
|
+
# @raise [Duracloud::Error] if the body is empty.
|
40
|
+
def self.create(id:, space_id:, body:)
|
41
|
+
new(id: id, space_id: space_id) do |content|
|
42
|
+
content.body = body
|
43
|
+
yield content if block_given?
|
44
|
+
content.save
|
19
45
|
end
|
46
|
+
end
|
20
47
|
|
21
|
-
|
22
|
-
|
23
|
-
|
48
|
+
attr_reader :id, :space_id
|
49
|
+
|
50
|
+
define_attribute_methods :content_type, :body, :md5
|
51
|
+
|
52
|
+
# Initialize a new piece of content
|
53
|
+
#
|
54
|
+
# @param id [String] The content ID
|
55
|
+
# @param space_id [String] The space ID
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# new(id: mycontent.txt", space_id: "myspace")
|
59
|
+
def initialize(id:, space_id:)
|
60
|
+
@id = id.freeze
|
61
|
+
@space_id = space_id.freeze
|
62
|
+
@body = nil
|
63
|
+
@content_type = nil
|
64
|
+
@md5 = nil
|
65
|
+
yield self if block_given?
|
24
66
|
end
|
25
67
|
|
26
|
-
|
27
|
-
|
68
|
+
def space
|
69
|
+
Space.find(space_id)
|
70
|
+
end
|
28
71
|
|
29
|
-
|
72
|
+
def inspect
|
73
|
+
"#<#{self.class} id=#{id.inspect}, space_id=#{space_id.inspect}>"
|
74
|
+
end
|
30
75
|
|
31
|
-
|
76
|
+
# @api private
|
77
|
+
# @raise [Duracloud::NotFoundError] the content does not exist in DuraCloud.
|
78
|
+
def load_body
|
79
|
+
response = Client.get_content(url)
|
80
|
+
@body = response.body # don't use setter
|
81
|
+
persisted!
|
82
|
+
end
|
32
83
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
raise Error, "Content requires either :url OR both :space_id AND :content_id."
|
39
|
-
end
|
40
|
-
options.values_at(:space_id, :content_id).join('/')
|
41
|
-
end
|
42
|
-
@url = URI(url).path # might not be exactly what we want, but it works
|
43
|
-
@body = options[:body] || options[:payload]
|
44
|
-
@md5 = options[:md5]
|
45
|
-
@content_type = options[:content_type]
|
46
|
-
self.properties = options[:properties].to_h
|
47
|
-
yield self if block_given?
|
84
|
+
def load_properties
|
85
|
+
super do |response|
|
86
|
+
# don't mark content_type as changed
|
87
|
+
@content_type = response.content_type
|
88
|
+
end
|
48
89
|
end
|
49
90
|
|
50
|
-
def
|
51
|
-
|
91
|
+
def body=(str_or_io)
|
92
|
+
val = read_string_or_io(str_or_io)
|
93
|
+
raise ArgumentError, "Cannot set body to empty string." if val.empty?
|
94
|
+
self.md5 = Digest::MD5.hexdigest(val)
|
95
|
+
body_will_change! if md5_changed?
|
96
|
+
@body = StringIO.new(val, "r")
|
52
97
|
end
|
53
98
|
|
54
|
-
def
|
55
|
-
|
99
|
+
def body
|
100
|
+
load_body if persisted? && empty?
|
101
|
+
@body
|
56
102
|
end
|
57
103
|
|
58
|
-
def
|
59
|
-
|
60
|
-
load_from_response(response)
|
61
|
-
self
|
104
|
+
def empty?
|
105
|
+
@body.nil? || @body.size == 0
|
62
106
|
end
|
63
|
-
alias_method :reload, :get
|
64
107
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
self
|
108
|
+
def content_type=(val)
|
109
|
+
content_type_will_change! unless val == @content_type
|
110
|
+
@content_type = val
|
69
111
|
end
|
70
|
-
alias_method :reload_properties, :get_properties
|
71
112
|
|
72
|
-
def
|
73
|
-
|
74
|
-
client.set_content_properties(url, properties: properties)
|
75
|
-
self
|
113
|
+
def content_type
|
114
|
+
@content_type
|
76
115
|
end
|
77
116
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
117
|
+
def md5
|
118
|
+
@md5
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def md5=(val)
|
124
|
+
md5_will_change! unless val == @md5
|
125
|
+
@md5 = val
|
126
|
+
end
|
127
|
+
|
128
|
+
def set_properties
|
129
|
+
headers = properties.to_h
|
130
|
+
headers["Content-Type"] = content_type if content_type_changed?
|
131
|
+
response = Client.set_content_properties(url, headers: headers)
|
132
|
+
# response.body is a text message -- log?
|
83
133
|
end
|
84
134
|
|
85
135
|
def store
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
options = {
|
92
|
-
payload: body, md5: md5, content_type: content_type, properties: properties
|
93
|
-
}
|
94
|
-
client.store_content(url, **options)
|
95
|
-
reload_properties
|
96
|
-
self
|
136
|
+
headers = { "Content-MD5" => md5,
|
137
|
+
"Content-Type" => content_type || "application/octet-stream" }
|
138
|
+
headers.merge!(properties)
|
139
|
+
response = Client.store_content(url, body: body, headers: headers)
|
140
|
+
# response.body is a text message -- log?
|
97
141
|
end
|
98
142
|
|
99
|
-
def
|
100
|
-
|
101
|
-
self.md5 = response.md5
|
102
|
-
self.content_type = response.content_type
|
103
|
-
load_properties_from_response(response)
|
143
|
+
def url
|
144
|
+
[space_id, id].join("/")
|
104
145
|
end
|
105
146
|
|
106
|
-
def
|
107
|
-
|
147
|
+
def properties_class
|
148
|
+
ContentProperties
|
108
149
|
end
|
109
150
|
|
110
|
-
def
|
111
|
-
|
151
|
+
def get_properties_response
|
152
|
+
Client.get_content_properties(url)
|
112
153
|
end
|
113
154
|
|
114
|
-
def
|
115
|
-
|
155
|
+
def do_delete
|
156
|
+
Client.delete_content(url)
|
116
157
|
end
|
117
158
|
|
118
|
-
def
|
119
|
-
|
159
|
+
def do_save
|
160
|
+
if !empty? && body_changed?
|
161
|
+
store
|
162
|
+
elsif persisted?
|
163
|
+
set_properties
|
164
|
+
else
|
165
|
+
raise Error, "Cannot store empty content."
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def read_string_or_io(str_or_io)
|
170
|
+
if str_or_io.respond_to?(:read)
|
171
|
+
read_io_like(str_or_io)
|
172
|
+
elsif str_or_io.respond_to?(:to_str)
|
173
|
+
str_or_io.to_str
|
174
|
+
else
|
175
|
+
raise ArgumentError, "IO-like or String-like argument required."
|
176
|
+
end
|
120
177
|
end
|
121
178
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
179
|
+
def read_io_like(io_like)
|
180
|
+
begin
|
181
|
+
io_like.rewind if io_like.respond_to?(:rewind)
|
182
|
+
io_like.read
|
183
|
+
ensure
|
184
|
+
io_like.rewind if io_like.respond_to?(:rewind)
|
185
|
+
end
|
125
186
|
end
|
187
|
+
|
126
188
|
end
|
127
189
|
end
|