aliyun-oss-sdk 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -0
  3. data/Gemfile +1 -0
  4. data/README.md +12 -10
  5. data/Rakefile +10 -0
  6. data/aliyun-oss.gemspec +2 -2
  7. data/lib/aliyun/oss.rb +4 -11
  8. data/lib/aliyun/oss/authorization.rb +59 -27
  9. data/lib/aliyun/oss/client.rb +137 -62
  10. data/lib/aliyun/oss/client/bucket_multiparts.rb +43 -0
  11. data/lib/aliyun/oss/client/bucket_objects.rb +111 -0
  12. data/lib/aliyun/oss/client/buckets.rb +50 -0
  13. data/lib/aliyun/oss/client/clients.rb +54 -0
  14. data/lib/aliyun/oss/error.rb +43 -0
  15. data/lib/aliyun/oss/http.rb +65 -23
  16. data/lib/aliyun/oss/struct.rb +26 -0
  17. data/lib/aliyun/oss/struct/bucket.rb +252 -0
  18. data/lib/aliyun/oss/struct/cors.rb +65 -0
  19. data/lib/aliyun/oss/struct/lifecycle.rb +73 -0
  20. data/lib/aliyun/oss/struct/logging.rb +33 -0
  21. data/lib/aliyun/oss/struct/multipart.rb +101 -0
  22. data/lib/aliyun/oss/struct/object.rb +71 -0
  23. data/lib/aliyun/oss/struct/part.rb +48 -0
  24. data/lib/aliyun/oss/struct/referer.rb +21 -0
  25. data/lib/aliyun/oss/struct/website.rb +13 -0
  26. data/lib/aliyun/oss/utils.rb +41 -1
  27. data/lib/aliyun/oss/version.rb +1 -1
  28. data/lib/aliyun/oss/xml_generator.rb +174 -0
  29. data/todo.md +5 -0
  30. data/wiki/bucket.md +20 -20
  31. data/wiki/cors.md +59 -0
  32. data/wiki/error.md +42 -31
  33. data/wiki/get_start.md +24 -7
  34. data/wiki/installation.md +2 -2
  35. data/wiki/lifecycle.md +75 -0
  36. data/wiki/multipart.md +7 -5
  37. data/wiki/object.md +16 -12
  38. metadata +48 -6
  39. data/lib/aliyun/oss/multipart/part.rb +0 -32
  40. data/lib/aliyun/oss/rule/cors.rb +0 -63
  41. data/lib/aliyun/oss/rule/lifecycle.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48824060a243cb4767d975c02b036ccedf0dba0a
4
- data.tar.gz: 0cdbddaf0f33abac43e4c5e5e05346df08773307
3
+ metadata.gz: 27f9032b8f7891bd84eb08e910d164194a8a72e5
4
+ data.tar.gz: 26dcaa8f67582786a2f62ea309b49c7a6b565eb4
5
5
  SHA512:
6
- metadata.gz: a38cd11c82084a0d7f61d0d96b8f4e69dd3cad0e37dc0e724561dd343065da244166e5752221c85c477d181082705ff68245df3750a5d87ed3ca5cbb49564e35
7
- data.tar.gz: ddb90c90e27a46a468c4fb7dbdff4683a6bd0f3f3d52626c71369704b227ad5d921e95f716a7cac3e345520b1b6fcb435f681cbec8eee9caa4a26c777359218b
6
+ metadata.gz: fc87a775fc990b532956c33a7cb1ff54cbb408fe24505b36a45c1859ec92ccabdb1356f120d6a98f1dd76d11c5a9f9a353cf193ce6dbc6053dbad566b3b85006
7
+ data.tar.gz: 3a461280cb56c0cff89c507b4ca6d21d742832d84a61d01a18ef5e79cdffa263176c625180ab6aa36518665bac6473409c3c503762a6061de96b82bef3a3846a
@@ -0,0 +1,17 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ Metrics/LineLength:
4
+ Max: 100
5
+ Enabled: false
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Style/DoubleNegation:
11
+ Enabled: false
12
+
13
+ Metrics/ClassLength:
14
+ Enabled: false
15
+
16
+ Style/AccessorMethodName:
17
+ Enabled: false
data/Gemfile CHANGED
@@ -3,3 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in ruby-oss-sdk.gemspec
4
4
  gemspec
5
5
  gem 'coveralls', require: false
6
+ gem 'simplecov', require: false
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Aliyun OSS SDK
2
2
 
3
3
  [![Build Status](https://travis-ci.org/zlx/aliyun-oss-sdk.svg)](https://travis-ci.org/zlx/aliyun-oss-sdk)
4
- [![Code Climate](https://codeclimate.com/github/zlx/aliyun-oss-sdk.png)](https://codeclimate.com/github/zlx/aliyun-oss-sdk)
4
+ [![Code Climate](https://codeclimate.com/github/zlx/aliyun-oss-sdk/badges/gpa.svg)](https://codeclimate.com/github/zlx/aliyun-oss-sdk)
5
5
  [![Coverage Status](https://coveralls.io/repos/zlx/aliyun-oss-sdk/badge.svg?branch=master&service=github)](https://coveralls.io/github/zlx/aliyun-oss-sdk?branch=master)
6
6
 
7
7
  -----
@@ -26,7 +26,7 @@ And run:
26
26
  bundle install
27
27
 
28
28
 
29
- ## Usage
29
+ ## Usage
30
30
 
31
31
  ### Quick Start
32
32
 
@@ -111,13 +111,13 @@ With Post Form, we need Post Policy to restrict permissions, here we provide two
111
111
  ##### Set the bucket properties ####
112
112
 
113
113
  # set cors for bucket
114
- rule = Aliyun::Oss::Rule::Cors.new({ allowed_methods: ['get'], allowed_origins: ['*'] })
114
+ rule = Aliyun::Oss::Struct::Cors.new({ allowed_methods: ['get'], allowed_origins: ['*'] })
115
115
  client.bucket_enable_cors([rule])
116
116
  client.bucket_disable_cors # Disable and remove existing cors
117
117
 
118
118
  # Set lifecycle for bucket
119
- rule1 = Aliyun::Oss::Rule::LifeCycle.new({ prefix: 'logs-prod-', days: 7, enable: true })
120
- rule2 = Aliyun::Oss::Rule::LifeCycle.new({ prefix: 'logs-dev', date: Time.now + 24*60*60, enable: true })
119
+ rule1 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-prod-', days: 7, enable: true })
120
+ rule2 = Aliyun::Oss::Struct::LifeCycle.new({ prefix: 'logs-dev', date: Time.now + 24*60*60, enable: true })
121
121
  client.bucket_enable_lifecycle([rule1, rule2])
122
122
  client.bucket_disable_lifecycle # Disable and remove existing lifecycle
123
123
 
@@ -188,9 +188,9 @@ With Post Form, we need Post Policy to restrict permissions, here we provide two
188
188
  client.bucket_list_parts("sample_multipart.data", "98A6524428734723BE8F81D72B5295EE")
189
189
 
190
190
  # Complete a Multipart Upload event
191
- part1 = Aliyun::Oss::Multipart::Part.new({ number: 1, etag: 'etag1' })
192
- part2 = Aliyun::Oss::Multipart::Part.new({ number: 2, etag: 'etag2' })
193
- part3 = Aliyun::Oss::Multipart::Part.new({ number: 3, etag: 'etag3' })
191
+ part1 = Aliyun::Oss::Struct::Part.new({ number: 1, etag: 'etag1' })
192
+ part2 = Aliyun::Oss::Struct::Part.new({ number: 2, etag: 'etag2' })
193
+ part3 = Aliyun::Oss::Struct::Part.new({ number: 3, etag: 'etag3' })
194
194
  client.bucket_complete_multipart("Exciting-Ruby.mp4", "98A6524428734723BE8F81D72B5295EE", [part1, part2, part3])
195
195
 
196
196
  # Abort a Multipart Upload event
@@ -215,10 +215,12 @@ Here is thr Ruby Document for this Library, use to find more usage for methods.
215
215
  Here are some more guides for help you. Welcome to advice.
216
216
 
217
217
  + [Installation](./wiki/installation.md)
218
- + [Get started](./wiki/get_start.md)
218
+ + [Getting Started](./wiki/get_start.md)
219
219
  + [Bucket](./wiki/bucket.md)
220
- + [Objects](./wiki/object.md)
220
+ + [Object](./wiki/object.md)
221
221
  + [Multipart Upload](./wiki/multipart.md)
222
+ + [CORS](./wiki/cors.md)
223
+ + [LifeCycle](./wiki/lifecycle.md)
222
224
  + [Error](./wiki/error.md)
223
225
 
224
226
 
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << 'test'
@@ -8,3 +9,12 @@ Rake::TestTask.new(:test) do |t|
8
9
  end
9
10
 
10
11
  task default: :test
12
+
13
+ task :test do
14
+ Rake::Task['test'].invoke
15
+ Rake::Task['rubocop'].invoke
16
+ end
17
+
18
+ RuboCop::RakeTask.new do |task|
19
+ task.fail_on_error = false
20
+ end
@@ -25,8 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'minitest'
27
27
  spec.add_development_dependency 'mocha'
28
- #spec.add_development_dependency 'pry-byebug'
28
+ spec.add_development_dependency 'pry-byebug'
29
29
  spec.add_development_dependency 'webmock'
30
30
  spec.add_development_dependency 'timecop'
31
- #spec.add_development_dependency 'simplecov'
31
+ spec.add_development_dependency 'rubocop'
32
32
  end
@@ -1,20 +1,13 @@
1
1
  require 'aliyun/oss/version'
2
+ require 'aliyun/oss/client'
3
+ require 'aliyun/oss/struct'
4
+ require 'aliyun/oss/error'
2
5
 
3
6
  module Aliyun
4
7
  module Oss
5
8
  autoload :Utils, 'aliyun/oss/utils'
6
- autoload :Client, 'aliyun/oss/client'
7
9
  autoload :Http, 'aliyun/oss/http'
8
- autoload :XmlBuilder, 'aliyun/oss/xml_builder'
9
10
  autoload :Authorization, 'aliyun/oss/authorization'
10
-
11
- module Rule
12
- autoload :LifeCycle, 'aliyun/oss/rule/lifecycle'
13
- autoload :Cors, 'aliyun/oss/rule/cors'
14
- end
15
-
16
- module Multipart
17
- autoload :Part, 'aliyun/oss/multipart/part'
18
- end
11
+ autoload :XmlGenerator, 'aliyun/oss/xml_generator'
19
12
  end
20
13
  end
@@ -13,7 +13,7 @@ module Aliyun
13
13
  # @see {https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-url Tempoorary Signature}
14
14
  #
15
15
  # @param secret_key [String] Secret Key
16
- # @param expires [Integer] the number of seconds since January 1, 1970 UTC. used to specified expired time
16
+ # @param expire_time [Integer] the number of seconds since January 1, 1970 UTC. used to specified expired time
17
17
  # @param [Hash] options other options
18
18
  # @option options [String] :key the object name
19
19
  # @option options [String] :bucket bucket name
@@ -31,7 +31,6 @@ module Aliyun
31
31
  #
32
32
  # @see {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject Post Object}
33
33
  #
34
- # @param secret_key [String] Secret Key
35
34
  # @param policy [Hash] Policy {https://docs.aliyun.com/#/pub/oss/api-reference/object&PostObject#menu7 Detail}
36
35
  #
37
36
  # @return [String]
@@ -57,6 +56,16 @@ module Aliyun
57
56
  #
58
57
  # @see {https://docs.aliyun.com/#/pub/oss/api-reference/access-control&signature-header Authorization}
59
58
  #
59
+ # @param access_key [String] Access Key
60
+ # @param secret_key [String] Secret Key
61
+ # @param options [Hash] Options
62
+ # @option options [String] :verb VERB, request method
63
+ # @option options [String] :date Request Time in formate: '%a, %d %b %Y %H:%M:%S GMT'
64
+ # @option options [String] :bucket Bucket Name
65
+ # @option options [String] :key Object Name
66
+ # @option options [Hash] :query Query key-value pair
67
+ # @option options [Hash] :headers Headers
68
+ #
60
69
  # @return [String] the authorization string
61
70
  def self.get_authorization(access_key, secret_key, options = {})
62
71
  content_string = concat_content_string(options[:verb], options[:date], options)
@@ -64,44 +73,66 @@ module Aliyun
64
73
  "#{PROVIDER} #{access_key}:#{signature_string.strip}"
65
74
  end
66
75
 
67
- private
68
-
69
76
  def self.concat_content_string(verb, time, options = {})
70
77
  headers = options.fetch(:headers, {})
71
78
 
72
- cononicalized_oss_headers = get_cononicalized_oss_headers(headers)
73
- cononicalized_resource = get_cononicalized_resource(*options.values_at(:bucket, :key, :query))
74
-
75
- if cononicalized_oss_headers
76
- [
77
- verb.upcase,
78
- headers['Content-MD5'],
79
- headers['Content-Type'],
80
- time,
81
- cononicalized_oss_headers,
82
- cononicalized_resource
83
- ].join("\n")
79
+ conon_headers = get_cononicalized_oss_headers(headers)
80
+ conon_resource = get_cononicalized_resource(
81
+ *options.values_at(:bucket, :key, :query)
82
+ )
83
+
84
+ join_values(verb.upcase, time, headers, conon_headers, conon_resource)
85
+ end
86
+
87
+ def self.join_values(verb, time, headers, conon_headers, resource)
88
+ if conon_headers
89
+ join_with_conon_headers(verb, time, headers, conon_headers, resource)
84
90
  else
85
- [
86
- verb.upcase,
87
- headers['Content-MD5'],
88
- headers['Content-Type'],
89
- time,
90
- cononicalized_resource
91
- ].join("\n")
91
+ join_without_conon_headers(verb, time, headers, resource)
92
92
  end
93
93
  end
94
94
 
95
+ def self.join_with_conon_headers(verb, time, headers, c_headers, resource)
96
+ [
97
+ verb,
98
+ headers['Content-MD5'],
99
+ headers['Content-Type'],
100
+ time,
101
+ c_headers,
102
+ resource
103
+ ].join("\n")
104
+ end
105
+
106
+ def self.join_without_conon_headers(verb, time, headers, resource)
107
+ [
108
+ verb,
109
+ headers['Content-MD5'],
110
+ headers['Content-Type'],
111
+ time,
112
+ resource
113
+ ].join("\n")
114
+ end
115
+
95
116
  def self.signature(secret_key, content_string)
96
117
  utf8_string = content_string.force_encoding('utf-8')
97
- Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret_key, utf8_string))
118
+ Base64.encode64(
119
+ OpenSSL::HMAC.digest(
120
+ OpenSSL::Digest::SHA1.new,
121
+ secret_key,
122
+ utf8_string
123
+ )
124
+ )
98
125
  end
99
126
 
100
127
  def self.get_cononicalized_oss_headers(headers)
101
- oss_headers = (headers || {}).select { |key, _| key.to_s.downcase.start_with?('x-oss-') }
128
+ oss_headers = (headers || {}).select do |key, _|
129
+ key.to_s.downcase.start_with?('x-oss-')
130
+ end
102
131
  return if oss_headers.empty?
103
132
 
104
- oss_headers.keys.sort.map { |key| "#{key.downcase}:#{oss_headers[key]}" }.join("\n")
133
+ oss_headers.keys.sort.map do |key|
134
+ "#{key.downcase}:#{oss_headers[key]}"
135
+ end.join("\n")
105
136
  end
106
137
 
107
138
  def self.get_cononicalized_resource(bucket, key, query)
@@ -110,7 +141,8 @@ module Aliyun
110
141
  cononicalized_resource += key if key
111
142
  return cononicalized_resource if query.nil? || query.empty?
112
143
 
113
- cononicalized_resource + '?' + query.keys.sort.map { |key| "#{key}=#{query[key]}" }.join('&')
144
+ query_str = query.keys.sort.map { |k| "#{k}=#{query[k]}" }.join('&')
145
+ cononicalized_resource + '?' + query_str
114
146
  end
115
147
  end
116
148
  end
@@ -1,12 +1,10 @@
1
- require 'base64'
2
- require 'openssl'
3
- require 'digest'
4
- require 'httparty'
1
+ require 'aliyun/oss/client/clients'
5
2
 
6
3
  module Aliyun
7
4
  module Oss
8
5
  class Client
9
- attr_reader :access_key, :secret_key, :bucket
6
+ attr_reader :access_key, :secret_key
7
+ attr_accessor :bucket
10
8
 
11
9
  # Initialize a object
12
10
  #
@@ -24,6 +22,8 @@ module Aliyun
24
22
  @secret_key = secret_key
25
23
  @options = options
26
24
  @bucket = options[:bucket]
25
+
26
+ @services = {}
27
27
  end
28
28
 
29
29
  # List buckets
@@ -72,13 +72,14 @@ module Aliyun
72
72
  # oss-cn-shenzhen,oss-cn-shanghai,oss-us-west-1 ,oss-ap-southeast-1
73
73
  # @param acl [String] Specify the bucket's access. (see #bucket_set_acl)
74
74
  #
75
+ # @raise [RequestError]
76
+ #
75
77
  # @return [Response]
76
78
  def bucket_create(name, location = 'oss-cn-hangzhou', acl = 'private')
77
79
  query = { 'acl' => true }
78
80
  headers = { 'x-oss-acl' => acl }
79
81
 
80
- configuration = { 'CreateBucketConfiguration' => { 'LocationConstraint' => location } }
81
- body = Utils.to_xml(configuration)
82
+ body = XmlGenerator.generate_create_bucket_xml(location)
82
83
 
83
84
  http.put('/', query: query, headers: headers, body: body, bucket: name, location: location)
84
85
  end
@@ -89,6 +90,8 @@ module Aliyun
89
90
  #
90
91
  # @param name [String] bucket name want to delete
91
92
  #
93
+ # @raise [RequestError]
94
+ #
92
95
  # @return [Response]
93
96
  def bucket_delete(name)
94
97
  http.delete('/', bucket: name)
@@ -99,6 +102,9 @@ module Aliyun
99
102
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketACL Put Bucket Acl
100
103
  #
101
104
  # @param acl [String] supported value: public-read-write | public-read | private
105
+ # @raise [RequestError]
106
+ #
107
+ # @return [Response]
102
108
  def bucket_set_acl(acl)
103
109
  query = { 'acl' => true }
104
110
  headers = { 'x-oss-acl' => acl }
@@ -111,12 +117,15 @@ module Aliyun
111
117
  #
112
118
  # @param target_bucket [String] specifies the bucket where you want Aliyun OSS to store server access logs.
113
119
  # @param target_prefix [String] this element lets you specify a prefix for the objects that the log files will be stored.
120
+ #
121
+ # @raise [RequestError]
122
+ #
123
+ # @return [Response]
114
124
  def bucket_enable_logging(target_bucket, target_prefix = nil)
115
125
  query = { 'logging' => true }
116
126
 
117
- logging = { 'TargetBucket' => target_bucket }
118
- logging.merge!('TargetPrefix' => target_prefix) if target_prefix
119
- body = Utils.to_xml('BucketLoggingStatus' => { 'LoggingEnabled' => logging })
127
+ body = XmlGenerator.generate_enable_logging_xml(target_bucket,
128
+ target_prefix)
120
129
 
121
130
  http.put('/', query: query, body: body, bucket: bucket)
122
131
  end
@@ -124,6 +133,10 @@ module Aliyun
124
133
  # Used to disable access logging.
125
134
  #
126
135
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucketLogging Delete Bucket Logging
136
+ #
137
+ # @raise [RequestError]
138
+ #
139
+ # @return [Response]
127
140
  def bucket_disable_logging
128
141
  query = { 'logging' => false }
129
142
  http.delete('/', query: query, bucket: bucket)
@@ -135,12 +148,14 @@ module Aliyun
135
148
  #
136
149
  # @param suffix [String] A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.
137
150
  # @param key [String] The object key name to use when a 4XX class error occurs
151
+ #
152
+ # @raise [RequestError]
153
+ #
154
+ # @return [Response]
138
155
  def bucket_enable_website(suffix, key = nil)
139
156
  query = { 'website' => true }
140
157
 
141
- website_configuration = { 'IndexDocument' => { 'Suffix' => suffix } }
142
- website_configuration.merge!('ErrorDocument' => { 'Key' => key }) if key
143
- body = Utils.to_xml('WebsiteConfiguration' => website_configuration)
158
+ body = XmlGenerator.generate_enable_website_xml(suffix, key)
144
159
 
145
160
  http.put('/', query: query, body: body, bucket: bucket)
146
161
  end
@@ -148,6 +163,10 @@ module Aliyun
148
163
  # Used to disable website hostted mode.
149
164
  #
150
165
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&DeleteBucketWebsite Delete Bucket Website
166
+ #
167
+ # @raise [RequestError]
168
+ #
169
+ # @return [Response]
151
170
  def bucket_disable_website
152
171
  query = { 'website' => false }
153
172
  http.delete('/', query: query, bucket: bucket)
@@ -160,17 +179,13 @@ module Aliyun
160
179
  # @param referers [Array<String>] white list for allowed referer.
161
180
  # @param allowed_empty [Boolean] whether allow empty refer.
162
181
  #
182
+ # @raise [RequestError]
183
+ #
163
184
  # @return [Response]
164
185
  def bucket_set_referer(referers = [], allowed_empty = false)
165
186
  query = { 'referer' => true }
166
187
 
167
- body = Utils.to_xml('RefererConfiguration' => {
168
- 'AllowEmptyReferer' => allowed_empty,
169
- 'RefererList' => {
170
- 'Referer' => referers
171
- }
172
- }
173
- )
188
+ body = XmlGenerator.generate_set_referer_xml(referers, allowed_empty)
174
189
 
175
190
  http.put('/', query: query, body: body, bucket: bucket)
176
191
  end
@@ -179,15 +194,25 @@ module Aliyun
179
194
  #
180
195
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&PutBucketLifecycle Put Bucket Lifecycle
181
196
  #
182
- # @param rules [Array<Rule::LifeCycle>] rules for lifecycle
197
+ # @param rules [Array<Aliyun::Oss::Struct::LifeCycle>] rules for lifecycle
198
+ #
199
+ # @raise [RequestError]
200
+ # @raise [Aliyun::Oss::InvalidLifeCycleRuleError]
201
+ # if rule invalid
183
202
  #
184
203
  # @return [Response]
185
204
  def bucket_enable_lifecycle(rules = [])
186
205
  query = { 'lifecycle' => true }
187
206
 
188
- body = Utils.to_xml('LifecycleConfiguration' => {
189
- 'Rule' => rules.map(&:to_hash)
190
- })
207
+ rules = Utils.wrap(rules)
208
+
209
+ rules.each do |rule|
210
+ unless rule.valid?
211
+ fail Aliyun::Oss::InvalidLifeCycleRuleError, rule.inspect
212
+ end
213
+ end
214
+
215
+ body = XmlGenerator.generate_lifecycle_rules(rules)
191
216
 
192
217
  http.put('/', query: query, body: body, bucket: bucket)
193
218
  end
@@ -204,15 +229,25 @@ module Aliyun
204
229
  #
205
230
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&PutBucketcors Put Bucket cors
206
231
  #
207
- # @param rules [Array<Rule::Cors>] array of rule
232
+ # @param rules [Array<Aliyun::Oss::Struct::Cors>] array of rule
233
+ #
234
+ # @raise [RequestError]
235
+ # @raise [InvalidCorsRule]
236
+ # if rule invalid
208
237
  #
209
238
  # @return [Response]
210
239
  def bucket_enable_cors(rules = [])
211
240
  query = { 'cors' => true }
212
241
 
213
- body = Utils.to_xml('CORSConfiguration' => {
214
- 'CORSRule' => rules.map(&:to_hash)
215
- })
242
+ rules = Utils.wrap(rules)
243
+
244
+ rules.each do |rule|
245
+ unless rule.valid?
246
+ fail Aliyun::Oss::InvalidCorsRuleError, rule.inspect
247
+ end
248
+ end
249
+
250
+ body = XmlGenerator.generate_cors_rules(rules)
216
251
 
217
252
  http.put('/', query: query, body: body, bucket: bucket)
218
253
  end
@@ -221,6 +256,8 @@ module Aliyun
221
256
  #
222
257
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&DeleteBucketcors Delete Bucket cors
223
258
  #
259
+ # @raise [RequestError]
260
+ #
224
261
  # @return [Response]
225
262
  def bucket_disable_cors
226
263
  query = { 'cors' => false }
@@ -231,21 +268,28 @@ module Aliyun
231
268
  #
232
269
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/cors&OptionObject OPTIONS Object
233
270
  #
271
+ # @param object_key [String] the object name want to visit.
234
272
  # @param origin [String] the requested source domain, denoting cross-domain request.
235
273
  # @param request_method [String] the actual request method will be used.
236
274
  # @param request_headers [Array<String>] the actual used headers except simple headers will be used.
237
- # @param object_name [String] the object name will be visit.
275
+ #
276
+ # @raise [RequestError]
238
277
  #
239
278
  # @return [Response]
240
- def bucket_preflight(origin, request_method, request_headers = [], object_name = nil)
241
- uri = object_name ? "/#{object_name}" : '/'
279
+ def bucket_preflight(object_key, origin, request_method, request_headers = [])
280
+ path = object_key ? "/#{object_key}" : '/'
281
+
282
+ headers = {
283
+ 'Origin' => origin,
284
+ 'Access-Control-Request-Method' => request_method
285
+ }
242
286
 
243
- headers = { 'Origin' => origin, 'Access-Control-Request-Method' => request_method }
244
287
  unless request_headers.empty?
245
- headers.merge!('Access-Control-Request-Headers' => request_headers.join(','))
288
+ value = request_headers.join(',')
289
+ headers.merge!('Access-Control-Request-Headers' => value)
246
290
  end
247
291
 
248
- http.options(uri, headers: headers, bucket: bucket, key: object_name)
292
+ http.options(path, headers: headers, bucket: bucket, key: object_key)
249
293
  end
250
294
 
251
295
  # Get ACL for bucket
@@ -272,6 +316,8 @@ module Aliyun
272
316
  #
273
317
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/bucket&GetBucketLogging Get Bucket Logging
274
318
  #
319
+ # @raise [RequestError]
320
+ #
275
321
  # @return [Response]
276
322
  def bucket_get_logging
277
323
  query = { 'logging' => true }
@@ -356,7 +402,9 @@ module Aliyun
356
402
  # @option options [String] :x-oss-copy-source-if-modified-since If the specified time is earlier than the source object last modification time, normal transfer ans return 200; Otherwise returns 304(not modified)
357
403
  # @option options [String] :x-oss-metadata-directive ('COPY') supported value: COPY, REPLACE;
358
404
  # @option options [String] :x-oss-server-side-encryption supported value: AES256
359
- # @option options [String] :x-oss-object-acl supported value: public-readprivatepublic-read-write
405
+ # @option options [String] :x-oss-object-acl supported value: public-read, private, public-read-write
406
+ #
407
+ # @raise [RequestError]
360
408
  #
361
409
  # @return [Response]
362
410
  def bucket_copy_object(key, source_bucket, source_key, headers = {})
@@ -377,6 +425,9 @@ module Aliyun
377
425
  # @param position [Integer] append to position of object
378
426
  # @option headers (see #bucket_create_object)
379
427
  #
428
+ # @raise [RequestError]
429
+ #
430
+ # @return [Response]
380
431
  def bucket_append_object(key, file, position = 0, headers = {})
381
432
  query = { 'append' => true, 'position' => position }
382
433
 
@@ -431,8 +482,7 @@ module Aliyun
431
482
  def bucket_delete_objects(keys, quiet = false)
432
483
  query = { 'delete' => true }
433
484
 
434
- key_objects = keys.map { |key| { 'Key' => key } }
435
- body = Utils.to_xml('Delete' => { 'Object' => key_objects, 'Quiet' => quiet })
485
+ body = XmlGenerator.generate_delete_objects_xml(keys, quiet)
436
486
 
437
487
  http.post('/', query: query, body: body, bucket: bucket)
438
488
  end
@@ -448,6 +498,8 @@ module Aliyun
448
498
  # @option headers [String] :If-Match If the specified ETag match the object ETag, normal transfer and return 200; Otherwise return 412(precondition)
449
499
  # @option headers [String] :If-None-Match If the specified ETag not match the object ETag, normal transfer and return 200; Otherwise return 304(Not Modified)
450
500
  #
501
+ # @raise [RequestError]
502
+ #
451
503
  # @return [Response]
452
504
  def bucket_get_meta_object(key, headers = {})
453
505
  http.head("/#{key}", headers: headers, bucket: bucket, key: key)
@@ -459,6 +511,8 @@ module Aliyun
459
511
  #
460
512
  # @param key [String] object name
461
513
  #
514
+ # @raise [RequestError]
515
+ #
462
516
  # @return [Response]
463
517
  def bucket_get_object_acl(key)
464
518
  query = { 'acl' => true }
@@ -472,6 +526,8 @@ module Aliyun
472
526
  # @param key [String] object name
473
527
  # @param acl [String] access value, supported value: private, public-read, public-read-write
474
528
  #
529
+ # @raise [RequestError]
530
+ #
475
531
  # @return [Response]
476
532
  def bucket_set_object_acl(key, acl)
477
533
  query = { 'acl' => true }
@@ -507,10 +563,14 @@ module Aliyun
507
563
  # @param upload_id [String] the upload ID return by #bucket_init_multipart
508
564
  # @param file [File, bin data] the upload data
509
565
  #
566
+ # @raise [RequestError]
567
+ # @raise [MultipartPartNumberEmptyError]
568
+ # @raise [MultipartUploadIdEmptyError]
569
+ #
510
570
  # @return [Response]
511
- def bucket_multipart_upload(key, number, upload_id, file)
512
- fail('number must present!') if number.nil?
513
- fail('upload_id must present!') if upload_id.nil? || upload_id.empty?
571
+ def bucket_multipart_upload(upload_id, key, number, file)
572
+ fail MultipartPartNumberEmptyError if number.nil?
573
+ fail MultipartUploadIdEmptyError if upload_id.nil? || upload_id.empty?
514
574
 
515
575
  query = { 'partNumber' => number.to_s, 'uploadId' => upload_id }
516
576
 
@@ -527,26 +587,27 @@ module Aliyun
527
587
  # @param options [Hash] options
528
588
  # @option options [String] :source_bucket the source bucket name
529
589
  # @option options [String] :source_key the source object name
530
- # @option options [String] :range the Range bytes, not set means the whole object
590
+ # @option options [String] :range the Range bytes, not set means the whole object, eg: bytes=100-6291756
531
591
  # @option options [String] :x-oss-copy-source-if-match If the specified ETag match the source object ETag, normal transfer and return 200; Otherwise return 412(precondition)
532
592
  # @option options [String] :x-oss-copy-source-if-none-match If the specified ETag not match the source object ETag, normal transfer and return 200; Otherwise return 304(Not Modified)
533
593
  # @option options [String] :x-oss-copy-source-if-unmodified-since If the specified time is equal to or later than the source object last modification time, normal transfer ans return 200; Otherwise returns 412(precondition)
534
594
  # @option options [String] :x-oss-copy-source-if-modified-since If the specified time is earlier than the source object last modification time, normal transfer ans return 200; Otherwise returns 304(not modified)
535
595
  #
596
+ # @raise [RequestError]
597
+ # @raise [MultipartSourceBucketEmptyError]
598
+ # @raise [MultipartSourceKeyEmptyError]
599
+ #
536
600
  # @return [Response]
537
- def bucket_multipart_copy_upload(key, number, upload_id, options = {})
538
- fail('source_bucket must present!') if options[:source_bucket].to_s.empty?
539
- fail('source_key must present!') if options[:source_key].to_s.empty?
540
-
541
- query = { 'partNumber' => number, 'uploadId' => upload_id }
601
+ def bucket_multipart_copy_upload(upload_id, key, number, options = {})
602
+ source_bucket = options.delete(:source_bucket).to_s
603
+ source_key = options.delete(:source_key).to_s
604
+ range = options.delete(:range)
542
605
 
543
- source_bucket = options.delete(:source_bucket)
544
- source_key = options.delete(:source_key)
606
+ fail MultipartSourceBucketEmptyError if source_bucket.empty?
607
+ fail MultipartSourceKeyEmptyError if source_key.empty?
545
608
 
546
- headers = {}
547
- headers.merge!('x-oss-copy-source' => "/#{source_bucket}/#{source_key}")
548
- headers.merge!('x-oss-copy-source-range' => options.delete(:range)) if options.key?(:range)
549
- headers.merge!(options)
609
+ query = { 'partNumber' => number, 'uploadId' => upload_id }
610
+ headers = copy_upload_headers(source_bucket, source_key, range, options)
550
611
 
551
612
  http.put("/#{key}", query: query, headers: headers, bucket: bucket, key: key)
552
613
  end
@@ -557,18 +618,20 @@ module Aliyun
557
618
  #
558
619
  # @param key [String] object name
559
620
  # @param upload_id [String] the upload ID return by #bucket_init_multipart
560
- # @param parts [Array<Multipart:Part>] parts
621
+ # @param parts [Array<Aliyun::Oss::Multipart:Part>] parts
622
+ #
623
+ # @raise [RequestError]
624
+ # @raise [MultipartPartsEmptyError]
625
+ # @raise [MultipartUploadIdEmptyError]
561
626
  #
562
627
  # @return [Response]
563
- def bucket_complete_multipart(key, upload_id, parts = [])
564
- fail('parts must present!') if parts.nil? || parts.empty?
565
- fail('upload_id must present!') if upload_id.nil?
628
+ def bucket_complete_multipart(upload_id, key, parts = [])
629
+ fail MultipartPartsEmptyError if parts.nil? || parts.empty?
630
+ fail MultipartUploadIdEmptyError if upload_id.nil?
566
631
 
567
632
  query = { 'uploadId' => upload_id }
568
633
 
569
- body = Utils.to_xml('CompleteMultipartUpload' => {
570
- 'Part' => parts.map(&:to_hash)
571
- })
634
+ body = XmlGenerator.generate_complete_multipart_xml(parts)
572
635
 
573
636
  http.post("/#{key}", query: query, body: body, bucket: bucket, key: key)
574
637
  end
@@ -583,8 +646,10 @@ module Aliyun
583
646
  # @param key [String] the object name
584
647
  # @param upload_id [String] the upload ID return by #bucket_init_multipart
585
648
  #
649
+ # @raise [RequestError]
650
+ #
586
651
  # @return [Response]
587
- def bucket_abort_multipart(key, upload_id)
652
+ def bucket_abort_multipart(upload_id, key)
588
653
  query = { 'uploadId' => upload_id }
589
654
  http.delete("/#{key}", query: query, bucket: bucket, key: key)
590
655
  end
@@ -616,14 +681,14 @@ module Aliyun
616
681
  # @see https://docs.aliyun.com/#/pub/oss/api-reference/multipart-upload&ListParts List Parts
617
682
  #
618
683
  # @param key [String] the object name
619
- # @param upload_id [String] the upload ID return by #bucket_init_multipart
684
+ # @param upload_id [Integer] the upload ID return by #bucket_init_multipart
620
685
  # @param options [Hash] options
621
686
  # @option options [Integer] :max-parts (1000) Limit number of parts, the maxinum should <= 1000
622
687
  # @option options [Integer] :part-number-marker Specify the start part, return parts which number large than the specified value
623
688
  # @option options [String] :encoding-type Encoding type used for unsupported character in xml 1.0
624
689
  #
625
690
  # @return [Response]
626
- def bucket_list_parts(key, upload_id, options = {})
691
+ def bucket_list_parts(upload_id, key, options = {})
627
692
  accepted_keys = ['max-parts', 'part-number-marker', 'encoding-type']
628
693
 
629
694
  query = Utils.hash_slice(options, *accepted_keys).merge('uploadId' => upload_id)
@@ -636,6 +701,16 @@ module Aliyun
636
701
  def http
637
702
  @http = Http.new(access_key, secret_key, @options[:host])
638
703
  end
704
+
705
+ def copy_upload_headers(source_bucket, source_key, range, options)
706
+ copy_source = "/#{source_bucket}/#{source_key}"
707
+
708
+ headers = {}
709
+ headers.merge!('x-oss-copy-source' => copy_source)
710
+ headers.merge!('x-oss-copy-source-range' => range) if range
711
+ headers.merge!(options)
712
+ headers
713
+ end
639
714
  end
640
715
  end
641
716
  end