duracloud-client 0.9.1 → 0.10.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.
@@ -1,46 +1,17 @@
1
- require "forwardable"
2
-
3
1
  module Duracloud
4
2
  class Client
5
- extend Forwardable
6
3
  extend RestMethods
7
4
  include RestMethods
8
5
 
9
- def self.execute(request_class, http_method, url, **options, &block)
10
- new.execute(request_class, http_method, url, **options, &block)
11
- end
12
-
13
- def self.configure
14
- yield Configuration
6
+ def self.execute(http_method, url, **options, &block)
7
+ new.execute(http_method, url, **options, &block)
15
8
  end
16
9
 
17
- attr_reader :config
18
-
19
- delegate [:host, :port, :user, :password, :base_url, :logger] => :config
20
-
21
- def initialize(**options)
22
- @config = Configuration.new(**options)
23
- end
24
-
25
- def execute(request_class, http_method, url, **options, &block)
26
- request = request_class.new(self, http_method, url, **options)
27
- response = request.execute(&block)
28
- handle_response(response)
29
- response
30
- end
31
-
32
- private
33
-
34
- def handle_response(response)
35
- logger.debug([self.class.to_s, response.request_method, response.url, response.request_query,
36
- response.status, response.reason].join(' '))
37
- if response.error?
38
- ErrorHandler.call(response)
39
- elsif %w(POST PUT DELETE).include?(response.request_method) &&
40
- response.plain_text? &&
41
- response.has_body?
42
- logger.info(response.body)
10
+ def execute(http_method, url, **options, &block)
11
+ Request.execute(http_method, url, **options, &block).tap do |response|
12
+ ResponseHandler.call(response)
43
13
  end
44
14
  end
15
+
45
16
  end
46
17
  end
@@ -4,10 +4,14 @@ require 'hashie'
4
4
  module Duracloud
5
5
  class CommandOptions < Hashie::Mash
6
6
 
7
- def initialize(*args)
8
- super()
7
+ def self.parse(*args)
8
+ new.parse(*args)
9
+ end
10
+
11
+ def parse(*args)
9
12
  self.command = args.shift if CLI::COMMANDS.include?(args.first)
10
13
  parser.parse!(args)
14
+ to_hash(symbolize_keys: true)
11
15
  end
12
16
 
13
17
  def print_version
@@ -113,6 +117,11 @@ module Duracloud
113
117
  opts.on("--[no-]all-spaces", "Get report for all spaces") do |v|
114
118
  self.all_spaces = v
115
119
  end
120
+
121
+ opts.on("-t", "--content-type CONTENT_TYPE",
122
+ "Media type of content to store") do |v|
123
+ self.content_type = v
124
+ end
116
125
  end
117
126
  end
118
127
 
@@ -33,6 +33,10 @@ module Duracloud
33
33
  ListItems.call(cli)
34
34
  end
35
35
 
36
+ def store(cli)
37
+ StoreContent.call(cli)
38
+ end
39
+
36
40
  end
37
41
  end
38
42
 
@@ -0,0 +1,13 @@
1
+ module Duracloud::Commands
2
+ class StoreContent < Command
3
+
4
+ def call
5
+ File.open(infile, "rb") do |body|
6
+ Duracloud::Content.create(space_id: space_id, store_id: store_id,
7
+ content_id: content_id, body: body,
8
+ md5: md5, content_type: content_type)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -1,5 +1,3 @@
1
- require "active_model"
2
-
3
1
  module Duracloud
4
2
  #
5
3
  # A piece of content in DuraCloud
@@ -13,6 +11,17 @@ module Duracloud
13
11
  COPY_SOURCE_STORE_HEADER = "x-dura-meta-copy-source-store"
14
12
  MANIFEST_EXT = ".dura-manifest"
15
13
 
14
+ property :space_id, required: true
15
+ property :content_id, required: true
16
+ property :store_id
17
+ property :body
18
+ property :md5
19
+ property :content_type
20
+ property :size
21
+ property :modified
22
+
23
+ alias_method :id, :content_id
24
+
16
25
  # Does the content exist in DuraCloud?
17
26
  # @return [Boolean] whether the content exists.
18
27
  # @raise [Duracloud::MessageDigestError] the provided digest in the :md5 keyword option,
@@ -45,10 +54,14 @@ module Duracloud
45
54
  new(**kwargs).save
46
55
  end
47
56
 
48
- attr_accessor :space_id, :content_id, :store_id,
49
- :body, :md5, :content_type, :size, :modified
50
- alias_method :id, :content_id
51
- validates_presence_of :space_id, :content_id
57
+ # Delete content from DuraCloud.
58
+ # @return [Duraclound::Content] the deleted content.
59
+ # @raise [Duracloud::NotFoundError] the space, content or store (if given) does not exist.
60
+ # @raise [Duracloud::MessageDigestError] the provided digest in the :md5 keyword option,
61
+ # if given, does not match the stored value.
62
+ def self.delete(**kwargs)
63
+ find(**kwargs).delete
64
+ end
52
65
 
53
66
  # Return the space associated with this content.
54
67
  # @return [Duracloud::Space] the space.
@@ -81,7 +94,7 @@ module Duracloud
81
94
  # The current instance still represents the original content.
82
95
  # @raise [Duracloud::Error]
83
96
  def copy(**args)
84
- dest = args.except(:force)
97
+ dest = args.reject { |k, v| k == :force }
85
98
  dest[:space_id] ||= space_id
86
99
  dest[:store_id] ||= store_id
87
100
  dest[:content_id] ||= content_id
@@ -1,13 +1,12 @@
1
1
  require 'nokogiri'
2
- require 'active_model'
2
+ require 'hashie'
3
3
 
4
4
  module Duracloud
5
- class ContentManifest
6
- include ActiveModel::Model
5
+ class ContentManifest < Hashie::Dash
7
6
 
8
- validates_presence_of :space_id, :manifest_id
9
-
10
- attr_accessor :space_id, :manifest_id, :store_id
7
+ property :space_id, required: true
8
+ property :manifest_id, required: true
9
+ property :store_id
11
10
 
12
11
  def self.find(**kwargs)
13
12
  new(**kwargs).tap do |manifest|
@@ -1,29 +1,47 @@
1
1
  require 'addressable/uri'
2
+ require 'httpclient'
2
3
 
3
4
  module Duracloud
4
5
  class Request
5
- attr_reader :client, :url, :http_method, :body, :headers, :query
6
6
 
7
- # @param client [Duracloud::Client] the client
7
+ attr_reader :url, :http_method, :body, :headers, :query
8
+
9
+ def self.execute(http_method, url, **options, &block)
10
+ request = new(http_method, url, **options)
11
+ request.execute(&block)
12
+ end
13
+
8
14
  # @param http_method [Symbol] the lower-case symbol corresponding to HTTP method
9
15
  # @param url [String] relative or absolute URL
10
16
  # @param body [String] the body of the request
11
17
  # @param headers [Hash] HTTP headers
12
18
  # @param query [Hash] Query string parameters
13
19
  # def initialize(client, http_method, url, body: nil, headers: nil, query: nil)
14
- def initialize(client, http_method, url, **options)
15
- @client = client
20
+ def initialize(http_method, url, **options)
16
21
  @http_method = http_method
17
22
  @url = Addressable::URI.parse(url).normalize.to_s
18
23
  set_options(options.dup)
19
24
  end
20
25
 
21
26
  def execute(&block)
22
- response_class.new original_response(&block)
27
+ Response.new(original_response(&block)).tap do |response|
28
+ log_request(response)
29
+ end
23
30
  end
24
31
 
25
32
  private
26
33
 
34
+ def log_request(response)
35
+ message = [ self.class.to_s,
36
+ response.request_method,
37
+ response.request_uri,
38
+ response.request_query,
39
+ response.status,
40
+ response.reason
41
+ ].join(' ')
42
+ Duracloud.logger.debug(message)
43
+ end
44
+
27
45
  def original_response(&block)
28
46
  connection.send(http_method,
29
47
  url,
@@ -41,16 +59,14 @@ module Duracloud
41
59
  @query = query.merge(options).reject { |k, v| v.to_s.empty? }
42
60
  end
43
61
 
44
- def base_path
45
- '/'
46
- end
47
-
48
- def response_class
49
- Response
50
- end
51
-
62
+ # @return [HTTPClient] An HTTP connection to DuraCloud.
63
+ # @note We are using HTTPClient because Net::HTTP capitalizes
64
+ # request header names which is incompatible with DuraCloud's
65
+ # custom case-sensitive content property headers (x-dura-meta-*).
52
66
  def connection
53
- @connection ||= Connection.new(client, base_path)
67
+ HTTPClient.new(base_url: Duracloud.base_url, force_basic_auth: Duracloud.auth?).tap do |conn|
68
+ conn.set_auth(Duracloud.base_url, Duracloud.user, Duracloud.password) if Duracloud.auth?
69
+ end
54
70
  end
55
71
  end
56
72
  end
@@ -8,12 +8,10 @@ module Duracloud
8
8
  attr_reader :original_response
9
9
 
10
10
  delegate [:header, :body, :code, :ok?, :redirect?, :status, :reason] => :original_response,
11
- :content_type => :header,
11
+ [:content_type, :request_method, :request_uri, :request_query] => :header,
12
12
  :empty? => :body
13
13
 
14
14
  def_delegator :header, :request_uri, :url
15
- def_delegator :header, :request_method
16
- def_delegator :header, :request_query
17
15
 
18
16
  def initialize(original_response)
19
17
  @original_response = original_response
@@ -49,5 +47,6 @@ module Duracloud
49
47
  def modified
50
48
  DateTime.parse(header["last-modified"].first) rescue nil
51
49
  end
50
+
52
51
  end
53
52
  end
@@ -0,0 +1,63 @@
1
+ module Duracloud
2
+ class ResponseHandler
3
+
4
+ def self.call(response)
5
+ new(response).call
6
+ end
7
+
8
+ attr_reader :response
9
+
10
+ def initialize(response)
11
+ @response = response
12
+ end
13
+
14
+ def call
15
+ handle_error
16
+ log_response
17
+ end
18
+
19
+ def log_response
20
+ if loggable_response_body?
21
+ Duracloud.logger.info(response.body)
22
+ end
23
+ end
24
+
25
+ def loggable_response_body?
26
+ %w(POST PUT DELETE).include?(response.request_method) &&
27
+ response.plain_text? &&
28
+ response.has_body?
29
+ end
30
+
31
+ def handle_error
32
+ if response.error?
33
+ raise exception, error_message
34
+ end
35
+ end
36
+
37
+ def error_message
38
+ if response.plain_text? && response.has_body?
39
+ response.body
40
+ else
41
+ [ response.status, response.reason ].join(' ')
42
+ end
43
+ end
44
+
45
+ def exception
46
+ case response.status
47
+ when 400
48
+ BadRequestError
49
+ when 404
50
+ NotFoundError
51
+ when 409
52
+ ConflictError
53
+ else
54
+ if response.status >= 500
55
+ ServerError
56
+ else
57
+ Error
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -125,8 +125,9 @@ module Duracloud
125
125
 
126
126
  private
127
127
 
128
- def durastore(*args, &block)
129
- execute(DurastoreRequest, *args, &block)
128
+ def durastore(http_method, url_path, **options, &block)
129
+ url = [ "durastore", url_path ].join("/")
130
+ execute(http_method, url, **options, &block)
130
131
  end
131
132
 
132
133
  def durastore_content(http_method, space_id, content_id, **options, &block)
@@ -1,5 +1,5 @@
1
- require "date"
2
- require "nokogiri"
1
+ require 'date'
2
+ require 'nokogiri'
3
3
 
4
4
  module Duracloud
5
5
  #
@@ -7,7 +7,10 @@ module Duracloud
7
7
  #
8
8
  class Space < AbstractEntity
9
9
 
10
- after_save :reset_acls
10
+ property :space_id, required: true
11
+ property :store_id
12
+
13
+ alias_method :id, :space_id
11
14
 
12
15
  # Max size of content item list for one request.
13
16
  # This limit is imposed by Duracloud.
@@ -117,12 +120,6 @@ module Duracloud
117
120
  end
118
121
  end
119
122
 
120
- attr_accessor :space_id, :store_id
121
- alias_method :id, :space_id
122
-
123
- after_save :reset_acls
124
- before_delete :reset_acls
125
-
126
123
  # @param space_id [String] the space ID
127
124
  # @param store_id [String] the store ID (optional)
128
125
  def initialize(space_id, store_id = nil)
@@ -249,11 +246,13 @@ module Duracloud
249
246
  end
250
247
 
251
248
  def do_delete
249
+ reset_acls
252
250
  Client.delete_space(id, **query)
253
251
  end
254
252
 
255
253
  def do_save
256
254
  persisted? ? update : create
255
+ reset_acls
257
256
  end
258
257
 
259
258
  def query
@@ -1,5 +1,4 @@
1
1
  require 'hashie'
2
- require 'active_support'
3
2
 
4
3
  module Duracloud
5
4
  class StorageReport < Hashie::Trash
@@ -15,17 +14,13 @@ module Duracloud
15
14
  @time ||= Time.at(timestamp / 1000.0).utc
16
15
  end
17
16
 
18
- def human_size
19
- ActiveSupport::NumberHelper.number_to_human_size(byte_count, prefix: :si)
20
- end
21
-
22
17
  def to_s
23
18
  <<-EOS
24
19
  Date: #{time}
25
20
  Space ID: #{space_id || "(all)"}
26
21
  Store ID: #{store_id}
27
22
  Objects: #{object_count}
28
- Total size: #{human_size} (#{byte_count} bytes)
23
+ Total size: #{byte_count} bytes
29
24
  EOS
30
25
  end
31
26
 
@@ -1,11 +1,10 @@
1
- require 'active_model'
2
1
  require 'tempfile'
3
2
  require 'csv'
4
3
  require 'fileutils'
4
+ require 'hashie'
5
5
 
6
6
  module Duracloud
7
- class SyncValidation
8
- include ActiveModel::Model
7
+ class SyncValidation < Hashie::Dash
9
8
 
10
9
  TWO_SPACES = ' '
11
10
  MD5_CSV_OPTS = { col_sep: TWO_SPACES }.freeze
@@ -15,7 +14,10 @@ module Duracloud
15
14
  CHANGED = "CHANGED"
16
15
  FOUND = "FOUND"
17
16
 
18
- attr_accessor :space_id, :content_dir, :store_id, :work_dir, :fast
17
+ property :space_id, required: true
18
+ property :content_dir, required: true
19
+ property :store_id
20
+ property :work_dir
19
21
 
20
22
  def self.call(*args)
21
23
  new(*args).call