aliyun-sdk 0.1.1

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.
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Aliyun
4
+ module OSS
5
+
6
+ VERSION = "0.1.1"
7
+
8
+ end # OSS
9
+ end # Aliyun
@@ -0,0 +1,595 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module Aliyun
8
+ module OSS
9
+
10
+ describe "Bucket" do
11
+
12
+ before :all do
13
+ @endpoint = 'oss.aliyuncs.com'
14
+ @protocol = Protocol.new(
15
+ Config.new(:endpoint => @endpoint,
16
+ :access_key_id => 'xxx', :access_key_secret => 'yyy'))
17
+ @bucket = 'rubysdk-bucket'
18
+ end
19
+
20
+ def request_path
21
+ @bucket + "." + @endpoint
22
+ end
23
+
24
+ def mock_location(location)
25
+ Nokogiri::XML::Builder.new do |xml|
26
+ xml.CreateBucketConfiguration {
27
+ xml.LocationConstraint location
28
+ }
29
+ end.to_xml
30
+ end
31
+
32
+ def mock_objects(objects, more = {})
33
+ Nokogiri::XML::Builder.new do |xml|
34
+ xml.ListBucketResult {
35
+ {
36
+ :prefix => 'Prefix',
37
+ :delimiter => 'Delimiter',
38
+ :limit => 'MaxKeys',
39
+ :marker => 'Marker',
40
+ :next_marker => 'NextMarker',
41
+ :truncated => 'IsTruncated',
42
+ :encoding => 'EncodingType'
43
+ }.map do |k, v|
44
+ xml.send(v, more[k]) if more[k]
45
+ end
46
+
47
+ objects.each do |o|
48
+ xml.Contents {
49
+ xml.Key o
50
+ xml.LastModified Time.now.to_s
51
+ xml.Type 'Normal'
52
+ xml.Size 1024
53
+ xml.StorageClass 'Standard'
54
+ xml.Etag 'etag'
55
+ xml.Owner {
56
+ xml.ID '10086'
57
+ xml.DisplayName 'CMCC'
58
+ }
59
+ }
60
+ end
61
+
62
+ (more[:common_prefixes] || []).each do |p|
63
+ xml.CommonPrefixes {
64
+ xml.Prefix p
65
+ }
66
+ end
67
+ }
68
+ end.to_xml
69
+ end
70
+
71
+ def mock_acl(acl)
72
+ Nokogiri::XML::Builder.new do |xml|
73
+ xml.AccessControlPolicy {
74
+ xml.Owner {
75
+ xml.ID 'owner_id'
76
+ xml.DisplayName 'owner_name'
77
+ }
78
+
79
+ xml.AccessControlList {
80
+ xml.Grant acl
81
+ }
82
+ }
83
+ end.to_xml
84
+ end
85
+
86
+ def mock_logging(opts)
87
+ Nokogiri::XML::Builder.new do |xml|
88
+ xml.BucketLoggingStatus {
89
+ if opts.enabled?
90
+ xml.LoggingEnabled {
91
+ xml.TargetBucket opts.target_bucket
92
+ xml.TargetPrefix opts.target_prefix
93
+ }
94
+ end
95
+ }
96
+ end.to_xml
97
+ end
98
+
99
+ def mock_website(opts)
100
+ Nokogiri::XML::Builder.new do |xml|
101
+ xml.WebsiteConfiguration {
102
+ xml.IndexDocument {
103
+ xml.Suffix opts.index
104
+ }
105
+ if opts.error
106
+ xml.ErrorDocument {
107
+ xml.Key opts.error
108
+ }
109
+ end
110
+ }
111
+ end.to_xml
112
+ end
113
+
114
+ def mock_referer(opts)
115
+ Nokogiri::XML::Builder.new do |xml|
116
+ xml.RefererConfiguration {
117
+ xml.AllowEmptyReferer opts.allow_empty?
118
+ xml.RefererList {
119
+ opts.whitelist.each do |r|
120
+ xml.Referer r
121
+ end
122
+ }
123
+ }
124
+ end.to_xml
125
+ end
126
+
127
+ def mock_lifecycle(rules)
128
+ Nokogiri::XML::Builder.new do |xml|
129
+ xml.LifecycleConfiguration {
130
+ rules.each do |r|
131
+ xml.Rule {
132
+ xml.ID r.id if r.id
133
+ xml.Status r.enabled? ? 'Enabled' : 'Disabled'
134
+ xml.Prefix r.prefix
135
+ xml.Expiration {
136
+ if r.expiry.is_a?(Date)
137
+ xml.Date Time.utc(r.expiry.year, r.expiry.month, r.expiry.day)
138
+ .iso8601.sub('Z', '.000Z')
139
+ else
140
+ xml.Days r.expiry.to_i
141
+ end
142
+ }
143
+ }
144
+ end
145
+ }
146
+ end.to_xml
147
+ end
148
+
149
+ def mock_cors(rules)
150
+ Nokogiri::XML::Builder.new do |xml|
151
+ xml.CORSConfiguration {
152
+ rules.each do |r|
153
+ xml.CORSRule {
154
+ r.allowed_origins.each do |x|
155
+ xml.AllowedOrigin x
156
+ end
157
+ r.allowed_methods.each do |x|
158
+ xml.AllowedMethod x
159
+ end
160
+ r.allowed_headers.each do |x|
161
+ xml.AllowedHeader x
162
+ end
163
+ r.expose_headers.each do |x|
164
+ xml.ExposeHeader x
165
+ end
166
+ xml.MaxAgeSeconds r.max_age_seconds if r.max_age_seconds
167
+ }
168
+ end
169
+ }
170
+ end.to_xml
171
+ end
172
+
173
+ def mock_error(code, message)
174
+ builder = Nokogiri::XML::Builder.new do |xml|
175
+ xml.Error {
176
+ xml.Code code
177
+ xml.Message message
178
+ xml.RequestId '0000'
179
+ }
180
+ end
181
+
182
+ builder.to_xml
183
+ end
184
+
185
+ context "Create bucket" do
186
+
187
+ it "should PUT to create bucket" do
188
+ stub_request(:put, request_path)
189
+
190
+ @protocol.create_bucket(@bucket)
191
+
192
+ expect(WebMock).to have_requested(:put, request_path)
193
+ .with(:body => nil, :query => {})
194
+ end
195
+
196
+ it "should set location when create bucket" do
197
+ location = 'oss-cn-hangzhou'
198
+
199
+ stub_request(:put, request_path).with(:body => mock_location(location))
200
+
201
+ @protocol.create_bucket(@bucket, :location => 'oss-cn-hangzhou')
202
+
203
+ expect(WebMock).to have_requested(:put, request_path)
204
+ .with(:body => mock_location(location), :query => {})
205
+ end
206
+ end # create bucket
207
+
208
+ context "List objects" do
209
+
210
+ it "should list all objects" do
211
+ stub_request(:get, request_path)
212
+
213
+ @protocol.list_objects(@bucket)
214
+
215
+ expect(WebMock).to have_requested(:get, request_path)
216
+ .with(:body => nil, :query => {})
217
+ end
218
+
219
+ it "should parse object response" do
220
+ return_objects = ['hello', 'world', 'foo/bar']
221
+ stub_request(:get, request_path)
222
+ .to_return(:body => mock_objects(return_objects))
223
+
224
+ objects, more = @protocol.list_objects(@bucket)
225
+
226
+ expect(WebMock).to have_requested(:get, request_path)
227
+ .with(:body => nil, :query => {})
228
+
229
+ expect(objects.map {|o| o.key}).to match_array(return_objects)
230
+ expect(more).to be_empty
231
+ end
232
+
233
+ it "should list objects with prefix & delimiter" do
234
+ # Webmock cannot capture the request_path encoded query parameters,
235
+ # so we use 'foo-bar' instead of 'foo/bar' to work around
236
+ # the problem
237
+ opts = {
238
+ :marker => 'foo-bar',
239
+ :prefix => 'foo-',
240
+ :delimiter => '-',
241
+ :limit => 10,
242
+ :encoding => KeyEncoding::URL}
243
+
244
+ query = opts.clone
245
+ query['max-keys'] = query.delete(:limit)
246
+ query['encoding-type'] = query.delete(:encoding)
247
+
248
+ stub_request(:get, request_path).with(:query => query)
249
+
250
+ @protocol.list_objects(@bucket, opts)
251
+
252
+ expect(WebMock).to have_requested(:get, request_path)
253
+ .with(:body => "", :query => query)
254
+ end
255
+
256
+ it "should parse object and common prefixes response" do
257
+ return_objects = ['hello', 'world', 'foo-bar']
258
+ return_more = {
259
+ :marker => 'foo-bar',
260
+ :prefix => 'foo-',
261
+ :delimiter => '-',
262
+ :limit => 10,
263
+ :encoding => KeyEncoding::URL,
264
+ :next_marker => 'foo-xxx',
265
+ :truncated => true,
266
+ :common_prefixes => ['foo/bar/', 'foo/xxx/']
267
+ }
268
+
269
+ opts = {
270
+ :marker => 'foo-bar',
271
+ :prefix => 'foo-',
272
+ :delimiter => '-',
273
+ :limit => 10,
274
+ :encoding => KeyEncoding::URL
275
+ }
276
+
277
+ query = opts.clone
278
+ query['max-keys'] = query.delete(:limit)
279
+ query['encoding-type'] = query.delete(:encoding)
280
+
281
+ stub_request(:get, request_path).with(:query => query).
282
+ to_return(:body => mock_objects(return_objects, return_more))
283
+
284
+ objects, more = @protocol.list_objects(@bucket, opts)
285
+
286
+ expect(WebMock).to have_requested(:get, request_path)
287
+ .with(:body => nil, :query => query)
288
+
289
+ expect(objects.map {|o| o.key}).to match_array(return_objects)
290
+ expect(more).to eq(return_more)
291
+ end
292
+
293
+ it "should decode object key" do
294
+ return_objects = ['中国のruby', 'world', 'foo/bar']
295
+ return_more = {
296
+ :marker => '杭州のruby',
297
+ :prefix => 'foo-',
298
+ :delimiter => '分隔のruby',
299
+ :limit => 10,
300
+ :encoding => KeyEncoding::URL,
301
+ :next_marker => '西湖のruby',
302
+ :truncated => true,
303
+ :common_prefixes => ['玉泉のruby', '苏堤のruby']
304
+ }
305
+
306
+ es_objects = [CGI.escape('中国のruby'), 'world', 'foo/bar']
307
+ es_more = {
308
+ :marker => CGI.escape('杭州のruby'),
309
+ :prefix => 'foo-',
310
+ :delimiter => CGI.escape('分隔のruby'),
311
+ :limit => 10,
312
+ :encoding => KeyEncoding::URL,
313
+ :next_marker => CGI.escape('西湖のruby'),
314
+ :truncated => true,
315
+ :common_prefixes => [CGI.escape('玉泉のruby'), CGI.escape('苏堤のruby')]
316
+ }
317
+
318
+ stub_request(:get, request_path)
319
+ .to_return(:body => mock_objects(es_objects, es_more))
320
+
321
+ objects, more = @protocol.list_objects(@bucket)
322
+
323
+ expect(WebMock).to have_requested(:get, request_path)
324
+ .with(:body => nil, :query => {})
325
+
326
+ expect(objects.map {|o| o.key}).to match_array(return_objects)
327
+ expect(more).to eq(return_more)
328
+ end
329
+ end # list objects
330
+
331
+ context "Delete bucket" do
332
+
333
+ it "should send DELETE reqeust" do
334
+ stub_request(:delete, request_path)
335
+
336
+ @protocol.delete_bucket(@bucket)
337
+
338
+ expect(WebMock).to have_requested(:delete, request_path)
339
+ .with(:body => nil, :query => {})
340
+ end
341
+
342
+ it "should raise Exception on error" do
343
+ code = "NoSuchBucket"
344
+ message = "The bucket to delete does not exist."
345
+
346
+ stub_request(:delete, request_path).to_return(
347
+ :status => 404, :body => mock_error(code, message))
348
+
349
+ expect {
350
+ @protocol.delete_bucket(@bucket)
351
+ }.to raise_error(Exception, message)
352
+ end
353
+ end # delete bucket
354
+
355
+ context "acl, logging, website, referer, lifecycle" do
356
+ it "should update acl" do
357
+ query = {'acl' => ''}
358
+ stub_request(:put, request_path).with(:query => query)
359
+
360
+ @protocol.put_bucket_acl(@bucket, ACL::PUBLIC_READ)
361
+
362
+ expect(WebMock).to have_requested(:put, request_path)
363
+ .with(:query => query, :body => nil)
364
+ end
365
+
366
+ it "should get acl" do
367
+ query = {'acl' => ''}
368
+ return_acl = ACL::PUBLIC_READ
369
+ stub_request(:get, request_path)
370
+ .with(:query => query)
371
+ .to_return(:body => mock_acl(return_acl))
372
+
373
+ acl = @protocol.get_bucket_acl(@bucket)
374
+
375
+ expect(WebMock).to have_requested(:get, request_path)
376
+ .with(:query => query, :body => nil)
377
+ expect(acl).to eq(return_acl)
378
+ end
379
+
380
+ it "should enable logging" do
381
+ query = {'logging' => ''}
382
+ stub_request(:put, request_path).with(:query => query)
383
+
384
+ logging_opts = BucketLogging.new(
385
+ :enable => true,
386
+ :target_bucket => 'target-bucket', :target_prefix => 'foo')
387
+ @protocol.put_bucket_logging(@bucket, logging_opts)
388
+
389
+ expect(WebMock).to have_requested(:put, request_path)
390
+ .with(:query => query, :body => mock_logging(logging_opts))
391
+ end
392
+
393
+ it "should disable logging" do
394
+ query = {'logging' => ''}
395
+ stub_request(:put, request_path).with(:query => query)
396
+
397
+ logging_opts = BucketLogging.new(:enable => false)
398
+ @protocol.put_bucket_logging(@bucket, logging_opts)
399
+
400
+ expect(WebMock).to have_requested(:put, request_path)
401
+ .with(:query => query, :body => mock_logging(logging_opts))
402
+ end
403
+
404
+ it "should get logging" do
405
+ query = {'logging' => ''}
406
+ logging_opts = BucketLogging.new(
407
+ :enable => true,
408
+ :target_bucket => 'target-bucket', :target_prefix => 'foo')
409
+
410
+ stub_request(:get, request_path)
411
+ .with(:query => query)
412
+ .to_return(:body => mock_logging(logging_opts))
413
+
414
+ logging = @protocol.get_bucket_logging(@bucket)
415
+
416
+ expect(WebMock).to have_requested(:get, request_path)
417
+ .with(:query => query, :body => nil)
418
+ expect(logging.to_s).to eq(logging_opts.to_s)
419
+ end
420
+
421
+ it "should delete logging" do
422
+ query = {'logging' => ''}
423
+ stub_request(:delete, request_path).with(:query => query)
424
+
425
+ @protocol.delete_bucket_logging(@bucket)
426
+
427
+ expect(WebMock).to have_requested(:delete, request_path)
428
+ .with(:query => query, :body => nil)
429
+ end
430
+
431
+ it "should update website" do
432
+ query = {'website' => ''}
433
+ stub_request(:put, request_path).with(:query => query)
434
+
435
+ website_opts = BucketWebsite.new(
436
+ :enable => true, :index => 'index.html', :error => 'error.html')
437
+ @protocol.put_bucket_website(@bucket, website_opts)
438
+
439
+ expect(WebMock).to have_requested(:put, request_path)
440
+ .with(:query => query, :body => mock_website(website_opts))
441
+ end
442
+
443
+ it "should get website" do
444
+ query = {'website' => ''}
445
+ website_opts = BucketWebsite.new(
446
+ :enable => true, :index => 'index.html', :error => 'error.html')
447
+
448
+ stub_request(:get, request_path)
449
+ .with(:query => query)
450
+ .to_return(:body => mock_website(website_opts))
451
+
452
+ opts = @protocol.get_bucket_website(@bucket)
453
+
454
+ expect(WebMock).to have_requested(:get, request_path)
455
+ .with(:query => query, :body => nil)
456
+ expect(opts.to_s).to eq(website_opts.to_s)
457
+ end
458
+
459
+ it "should delete website" do
460
+ query = {'website' => ''}
461
+ stub_request(:delete, request_path).with(:query => query)
462
+
463
+ @protocol.delete_bucket_website(@bucket)
464
+
465
+ expect(WebMock).to have_requested(:delete, request_path)
466
+ .with(:query => query, :body => nil)
467
+ end
468
+
469
+ it "should update referer" do
470
+ query = {'referer' => ''}
471
+ stub_request(:put, request_path).with(:query => query)
472
+
473
+ referer_opts = BucketReferer.new(
474
+ :allow_empty => true, :whitelist => ['xxx', 'yyy'])
475
+ @protocol.put_bucket_referer(@bucket, referer_opts)
476
+
477
+ expect(WebMock).to have_requested(:put, request_path)
478
+ .with(:query => query, :body => mock_referer(referer_opts))
479
+ end
480
+
481
+ it "should get referer" do
482
+ query = {'referer' => ''}
483
+ referer_opts = BucketReferer.new(
484
+ :allow_empty => true, :whitelist => ['xxx', 'yyy'])
485
+
486
+ stub_request(:get, request_path)
487
+ .with(:query => query)
488
+ .to_return(:body => mock_referer(referer_opts))
489
+
490
+ opts = @protocol.get_bucket_referer(@bucket)
491
+
492
+ expect(WebMock).to have_requested(:get, request_path)
493
+ .with(:query => query, :body => nil)
494
+ expect(opts.to_s).to eq(referer_opts.to_s)
495
+ end
496
+
497
+ it "should update lifecycle" do
498
+ query = {'lifecycle' => ''}
499
+ stub_request(:put, request_path).with(:query => query)
500
+
501
+ rules = (1..5).map do |i|
502
+ LifeCycleRule.new(
503
+ :id => i, :enable => i % 2 == 0, :prefix => "foo#{i}",
504
+ :expiry => (i % 2 == 1 ? Date.today : 10 + i))
505
+ end
506
+
507
+ @protocol.put_bucket_lifecycle(@bucket, rules)
508
+
509
+ expect(WebMock).to have_requested(:put, request_path)
510
+ .with(:query => query, :body => mock_lifecycle(rules))
511
+ end
512
+
513
+ it "should get lifecycle" do
514
+ query = {'lifecycle' => ''}
515
+ return_rules = (1..5).map do |i|
516
+ LifeCycleRule.new(
517
+ :id => i, :enable => i % 2 == 0, :prefix => "foo#{i}",
518
+ :expiry => (i % 2 == 1 ? Date.today : 10 + i))
519
+ end
520
+
521
+ stub_request(:get, request_path)
522
+ .with(:query => query)
523
+ .to_return(:body => mock_lifecycle(return_rules))
524
+
525
+ rules = @protocol.get_bucket_lifecycle(@bucket)
526
+
527
+ expect(WebMock).to have_requested(:get, request_path)
528
+ .with(:query => query, :body => nil)
529
+ expect(rules.map(&:to_s)).to match_array(return_rules.map(&:to_s))
530
+ end
531
+
532
+ it "should delete lifecycle" do
533
+ query = {'lifecycle' => ''}
534
+ stub_request(:delete, request_path).with(:query => query)
535
+
536
+ @protocol.delete_bucket_lifecycle(@bucket)
537
+
538
+ expect(WebMock).to have_requested(:delete, request_path)
539
+ .with(:query => query, :body => nil)
540
+ end
541
+
542
+ it "should set cors" do
543
+ query = {'cors' => ''}
544
+ stub_request(:put, request_path).with(:query => query)
545
+
546
+ rules = (1..5).map do |i|
547
+ CORSRule.new(
548
+ :allowed_origins => (1..3).map {|x| "origin-#{x}"},
549
+ :allowed_methods => ['PUT', 'GET'],
550
+ :allowed_headers => (1..3).map {|x| "header-#{x}"},
551
+ :expose_headers => (1..3).map {|x| "header-#{x}"})
552
+ end
553
+ @protocol.set_bucket_cors(@bucket, rules)
554
+
555
+ expect(WebMock).to have_requested(:put, request_path)
556
+ .with(:query => query, :body => mock_cors(rules))
557
+ end
558
+
559
+ it "should get cors" do
560
+ query = {'cors' => ''}
561
+ return_rules = (1..5).map do |i|
562
+ CORSRule.new(
563
+ :allowed_origins => (1..3).map {|x| "origin-#{x}"},
564
+ :allowed_methods => ['PUT', 'GET'],
565
+ :allowed_headers => (1..3).map {|x| "header-#{x}"},
566
+ :expose_headers => (1..3).map {|x| "header-#{x}"})
567
+ end
568
+
569
+ stub_request(:get, request_path)
570
+ .with(:query => query)
571
+ .to_return(:body => mock_cors(return_rules))
572
+
573
+ rules = @protocol.get_bucket_cors(@bucket)
574
+
575
+ expect(WebMock).to have_requested(:get, request_path)
576
+ .with(:query => query, :body => nil)
577
+ expect(rules.map(&:to_s)).to match_array(return_rules.map(&:to_s))
578
+ end
579
+
580
+ it "should delete cors" do
581
+ query = {'cors' => ''}
582
+
583
+ stub_request(:delete, request_path).with(:query => query)
584
+
585
+ @protocol.delete_bucket_cors(@bucket)
586
+ expect(WebMock).to have_requested(:delete, request_path)
587
+ .with(:query => query, :body => nil)
588
+ end
589
+
590
+ end # acl, logging, cors, etc
591
+
592
+ end # Bucket
593
+
594
+ end # OSS
595
+ end # Aliyun