duracloud-client 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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