waz-storage 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/CHANGELOG.rdoc +72 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +19 -0
- data/README.rdoc +307 -0
- data/lib/waz-storage.rb +9 -1
- data/lib/waz/blobs/blob_object.rb +2 -1
- data/lib/waz/blobs/container.rb +2 -1
- data/lib/waz/blobs/service.rb +16 -16
- data/lib/waz/queues/service.rb +8 -8
- data/lib/waz/storage/core_service.rb +1 -1
- data/rakefile +4 -34
- data/tests/configuration.rb +14 -0
- data/tests/waz/blobs/blob_object_test.rb +80 -0
- data/tests/waz/blobs/container_test.rb +162 -0
- data/tests/waz/blobs/service_test.rb +282 -0
- data/tests/waz/queues/message_test.rb +33 -0
- data/tests/waz/queues/queue_test.rb +206 -0
- data/tests/waz/queues/service_test.rb +299 -0
- data/tests/waz/storage/base_tests.rb +81 -0
- data/tests/waz/storage/shared_key_core_service_test.rb +142 -0
- data/tests/waz/tables/service_test.rb +614 -0
- data/tests/waz/tables/table_test.rb +98 -0
- data/waz-storage.gemspec +27 -0
- metadata +93 -16
- data/lib/waz/storage/version.rb +0 -11
data/lib/waz-storage.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
|
1
|
+
require 'time'
|
2
|
+
require 'cgi'
|
3
|
+
require 'base64'
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'rexml/xpath'
|
6
|
+
require 'restclient'
|
7
|
+
require 'hmac-sha2'
|
8
|
+
require 'net/http'
|
9
|
+
|
2
10
|
app_files = File.expand_path(File.join(File.dirname(__FILE__), 'waz', 'storage', '*.rb'))
|
3
11
|
Dir[app_files].each(&method(:load))
|
4
12
|
|
@@ -35,7 +35,7 @@ module WAZ
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
attr_accessor :name, :url, :content_type, :snapshot_date
|
38
|
+
attr_accessor :name, :url, :content_type, :snapshot_date, :railsetag
|
39
39
|
|
40
40
|
# Creates a new instance of the Blob object. This constructor is internally used by the Container
|
41
41
|
# it's initialized thru a hash that's received as parameter. It has the following requirements:
|
@@ -49,6 +49,7 @@ module WAZ
|
|
49
49
|
self.url = options[:url]
|
50
50
|
self.content_type = options[:content_type]
|
51
51
|
self.snapshot_date = options[:snapshot_date]
|
52
|
+
self.railsetag = options[:railsetag]
|
52
53
|
end
|
53
54
|
|
54
55
|
# Returns the blob properties from Windows Azure. This properties always come as HTTP Headers and they include standard headers like
|
data/lib/waz/blobs/container.rb
CHANGED
@@ -145,7 +145,8 @@ module WAZ
|
|
145
145
|
properties = self.class.service_instance.get_blob_properties("#{self.name}/#{blob_name}")
|
146
146
|
return BlobObject.new(:name => blob_name,
|
147
147
|
:url => self.class.service_instance.generate_request_uri("#{self.name}/#{blob_name}"),
|
148
|
-
:content_type => properties[:content_type]
|
148
|
+
:content_type => properties[:content_type],
|
149
|
+
:railsetag => properties[:x_ms_meta_railsetag])
|
149
150
|
rescue RestClient::ResourceNotFound
|
150
151
|
return nil
|
151
152
|
end
|
data/lib/waz/blobs/service.rb
CHANGED
@@ -7,12 +7,12 @@ module WAZ
|
|
7
7
|
|
8
8
|
# Creates a container on the current Windows Azure Storage account.
|
9
9
|
def create_container(container_name)
|
10
|
-
execute :put, container_name, {:restype => 'container'}, {:x_ms_version => '
|
10
|
+
execute :put, container_name, {:restype => 'container'}, {:x_ms_version => '2011-08-18'}
|
11
11
|
end
|
12
12
|
|
13
13
|
# Retrieves all the properties existing on the container.
|
14
14
|
def get_container_properties(container_name)
|
15
|
-
execute(:get, container_name, {:restype => 'container'}, {:x_ms_version => '
|
15
|
+
execute(:get, container_name, {:restype => 'container'}, {:x_ms_version => '2011-08-18'}).headers
|
16
16
|
end
|
17
17
|
|
18
18
|
# Set the container properties (metadata).
|
@@ -20,14 +20,14 @@ module WAZ
|
|
20
20
|
# Remember that custom properties should be named as :x_ms_meta_{propertyName} in order
|
21
21
|
# to have Windows Azure to persist them.
|
22
22
|
def set_container_properties(container_name, properties = {})
|
23
|
-
execute :put, container_name, { :restype => 'container', :comp => 'metadata' }, properties.merge!({:x_ms_version => '
|
23
|
+
execute :put, container_name, { :restype => 'container', :comp => 'metadata' }, properties.merge!({:x_ms_version => '2011-08-18'})
|
24
24
|
end
|
25
25
|
|
26
26
|
# Retrieves the value of the :x_ms_prop_publicaccess header from the
|
27
27
|
# container properties indicating whether the container is publicly
|
28
28
|
# accessible or not.
|
29
29
|
def get_container_acl(container_name)
|
30
|
-
headers = execute(:get, container_name, { :restype => 'container', :comp => 'acl' }, {:x_ms_version => '
|
30
|
+
headers = execute(:get, container_name, { :restype => 'container', :comp => 'acl' }, {:x_ms_version => '2011-08-18'}).headers
|
31
31
|
headers[:x_ms_blob_public_access]
|
32
32
|
end
|
33
33
|
|
@@ -37,7 +37,7 @@ module WAZ
|
|
37
37
|
#
|
38
38
|
# Default is _false_
|
39
39
|
def set_container_acl(container_name, public_available = WAZ::Blobs::BlobSecurity::Private)
|
40
|
-
publicity = {:x_ms_version => '
|
40
|
+
publicity = {:x_ms_version => '2011-08-18' }
|
41
41
|
publicity[:x_ms_blob_public_access] = public_available unless public_available == WAZ::Blobs::BlobSecurity::Private
|
42
42
|
execute :put, container_name, { :restype => 'container', :comp => 'acl' }, publicity
|
43
43
|
end
|
@@ -57,12 +57,12 @@ module WAZ
|
|
57
57
|
|
58
58
|
# Deletes the given container from the Windows Azure Storage account.
|
59
59
|
def delete_container(container_name)
|
60
|
-
execute :delete, container_name, {:restype => 'container'}, {:x_ms_version => '
|
60
|
+
execute :delete, container_name, {:restype => 'container'}, {:x_ms_version => '2011-08-18'}
|
61
61
|
end
|
62
62
|
|
63
63
|
# Lists all the blobs inside the given container.
|
64
64
|
def list_blobs(container_name)
|
65
|
-
content = execute(:get, container_name, { :restype => 'container', :comp => 'list'}, {:x_ms_version => '
|
65
|
+
content = execute(:get, container_name, { :restype => 'container', :comp => 'list'}, {:x_ms_version => '2011-08-18'})
|
66
66
|
doc = REXML::Document.new(content)
|
67
67
|
containers = []
|
68
68
|
REXML::XPath.each(doc, '//Blob/') do |item|
|
@@ -82,7 +82,7 @@ module WAZ
|
|
82
82
|
#
|
83
83
|
# metadata is a hash that stores all the properties that you want to add to the blob when creating it.
|
84
84
|
def put_blob(path, payload, content_type = "application/octet-stream", metadata = {})
|
85
|
-
default_headers = {"Content-Type" => content_type, :x_ms_version => "
|
85
|
+
default_headers = {"Content-Type" => content_type, :x_ms_version => "2011-08-18", :x_ms_blob_type => "BlockBlob", :x_ms_meta_railsetag => Digest::MD5.hexdigest(payload)}
|
86
86
|
execute :put, path, nil, metadata.merge(default_headers), payload
|
87
87
|
end
|
88
88
|
|
@@ -94,38 +94,38 @@ module WAZ
|
|
94
94
|
#
|
95
95
|
# metadata is a hash that stores all the properties that you want to add to the blob when creating it.
|
96
96
|
def put_block_list(path, blockids, content_type = "application/octet-stream", metadata = {})
|
97
|
-
default_headers = {"Content-Type" => content_type, :x_ms_version => "
|
97
|
+
default_headers = {"Content-Type" => content_type, :x_ms_version => "2011-08-18"}
|
98
98
|
execute :put, path, { :comp => 'blocklist' }, metadata.merge(default_headers), '<?xml version="1.0" encoding="utf-8"?><BlockList>' + blockids.map {|id| "<Latest>#{id.rstrip}</Latest>"}.join + '</BlockList>'
|
99
99
|
end
|
100
100
|
|
101
101
|
# Retrieves a blob (content + headers) from the current path.
|
102
102
|
def get_blob(path, options = {})
|
103
|
-
execute :get, path, options, {:x_ms_version => "
|
103
|
+
execute :get, path, options, {:x_ms_version => "2011-08-18"}
|
104
104
|
end
|
105
105
|
|
106
106
|
# Deletes the blob existing on the current path.
|
107
107
|
def delete_blob(path)
|
108
|
-
execute :delete, path, nil, {:x_ms_version => "
|
108
|
+
execute :delete, path, nil, {:x_ms_version => "2011-08-18"}
|
109
109
|
end
|
110
110
|
|
111
111
|
# Retrieves the properties associated with the blob at the given path.
|
112
112
|
def get_blob_properties(path, options = {})
|
113
|
-
execute(:head, path, options, {:x_ms_version => "
|
113
|
+
execute(:head, path, options, {:x_ms_version => "2011-08-18"}).headers
|
114
114
|
end
|
115
115
|
|
116
116
|
# Sets the properties (metadata) associated to the blob at given path.
|
117
117
|
def set_blob_properties(path, properties ={})
|
118
|
-
execute :put, path, { :comp => 'properties' }, properties.merge({:x_ms_version => "
|
118
|
+
execute :put, path, { :comp => 'properties' }, properties.merge({:x_ms_version => "2011-08-18"})
|
119
119
|
end
|
120
120
|
|
121
121
|
# Set user defined metadata - overwrites any previous metadata key:value pairs
|
122
122
|
def set_blob_metadata(path, metadata = {})
|
123
|
-
execute :put, path, { :comp => 'metadata' }, metadata.merge({:x_ms_version => "
|
123
|
+
execute :put, path, { :comp => 'metadata' }, metadata.merge({:x_ms_version => "2011-08-18"})
|
124
124
|
end
|
125
125
|
|
126
126
|
# Copies a blob within the same account (not necessarily to the same container)
|
127
127
|
def copy_blob(source_path, dest_path)
|
128
|
-
execute :put, dest_path, nil, { :x_ms_version => "
|
128
|
+
execute :put, dest_path, nil, { :x_ms_version => "2011-08-18", :x_ms_copy_source => canonicalize_message(source_path) }
|
129
129
|
end
|
130
130
|
|
131
131
|
# Adds a block to the block list of the given blob
|
@@ -149,7 +149,7 @@ module WAZ
|
|
149
149
|
|
150
150
|
# Creates a read-only snapshot of a blob as it looked like in time.
|
151
151
|
def snapshot_blob(path)
|
152
|
-
execute(:put, path, { :comp => 'snapshot' }, {:x_ms_version => "
|
152
|
+
execute(:put, path, { :comp => 'snapshot' }, {:x_ms_version => "2011-08-18"}).headers[:x_ms_snapshot]
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
data/lib/waz/queues/service.rb
CHANGED
@@ -10,7 +10,7 @@ module WAZ
|
|
10
10
|
# When the options :include => 'metadata' is passed it returns
|
11
11
|
# the corresponding metadata for each queue on the listing.
|
12
12
|
def list_queues(options ={})
|
13
|
-
content = execute(:get, nil, { :comp => 'list' }.merge!(options), { :x_ms_version => "
|
13
|
+
content = execute(:get, nil, { :comp => 'list' }.merge!(options), { :x_ms_version => "2011-08-18" })
|
14
14
|
doc = REXML::Document.new(content)
|
15
15
|
queues = []
|
16
16
|
|
@@ -31,22 +31,22 @@ module WAZ
|
|
31
31
|
# Creates a queue on the current storage account. Throws WAZ::Queues::QueueAlreadyExists when
|
32
32
|
# existing metadata and given metadata differ.
|
33
33
|
def create_queue(queue_name, metadata = {})
|
34
|
-
execute(:put, queue_name, nil, metadata.merge!(:x_ms_version => '
|
34
|
+
execute(:put, queue_name, nil, metadata.merge!(:x_ms_version => '2011-08-18'))
|
35
35
|
end
|
36
36
|
|
37
37
|
# Deletes the given queue from the current storage account.
|
38
38
|
def delete_queue(queue_name)
|
39
|
-
execute(:delete, queue_name, {}, {:x_ms_version => '
|
39
|
+
execute(:delete, queue_name, {}, {:x_ms_version => '2011-08-18'})
|
40
40
|
end
|
41
41
|
|
42
42
|
# Gets the given queue metadata.
|
43
43
|
def get_queue_metadata(queue_name)
|
44
|
-
execute(:head, queue_name, { :comp => 'metadata'}, :x_ms_version => '
|
44
|
+
execute(:head, queue_name, { :comp => 'metadata'}, :x_ms_version => '2011-08-18').headers
|
45
45
|
end
|
46
46
|
|
47
47
|
# Sets the given queue metadata.
|
48
48
|
def set_queue_metadata(queue_name, metadata = {})
|
49
|
-
execute(:put, queue_name, { :comp => 'metadata' }, metadata.merge!(:x_ms_version => '
|
49
|
+
execute(:put, queue_name, { :comp => 'metadata' }, metadata.merge!(:x_ms_version => '2011-08-18'))
|
50
50
|
end
|
51
51
|
|
52
52
|
# Enqueues a message on the current queue.
|
@@ -55,7 +55,7 @@ module WAZ
|
|
55
55
|
# is omitted, the default time-to-live is 7 days.
|
56
56
|
def enqueue(queue_name, message_payload, ttl = 604800)
|
57
57
|
payload = "<?xml version=\"1.0\" encoding=\"utf-8\"?><QueueMessage><MessageText>#{message_payload}</MessageText></QueueMessage>"
|
58
|
-
execute(:post, "#{queue_name}/messages", { :messagettl => ttl }, { 'Content-Type' => 'application/xml', :x_ms_version => "
|
58
|
+
execute(:post, "#{queue_name}/messages", { :messagettl => ttl }, { 'Content-Type' => 'application/xml', :x_ms_version => "2011-08-18"}, payload)
|
59
59
|
end
|
60
60
|
|
61
61
|
# Locks N messages (1 default) from the given queue.
|
@@ -66,7 +66,7 @@ module WAZ
|
|
66
66
|
def get_messages(queue_name, options = {})
|
67
67
|
raise WAZ::Queues::OptionOutOfRange, {:name => :num_of_messages, :min => 1, :max => 32} if (options.keys.include?(:num_of_messages) && (options[:num_of_messages].to_i < 1 || options[:num_of_messages].to_i > 32))
|
68
68
|
raise WAZ::Queues::OptionOutOfRange, {:name => :visibility_timeout, :min => 1, :max => 7200} if (options.keys.include?(:visibility_timeout) && (options[:visibility_timeout].to_i < 1 || options[:visibility_timeout].to_i > 7200))
|
69
|
-
content = execute(:get, "#{queue_name}/messages", options, {:x_ms_version => "
|
69
|
+
content = execute(:get, "#{queue_name}/messages", options, {:x_ms_version => "2011-08-18"})
|
70
70
|
doc = REXML::Document.new(content)
|
71
71
|
messages = []
|
72
72
|
REXML::XPath.each(doc, '//QueueMessage/') do |item|
|
@@ -99,7 +99,7 @@ module WAZ
|
|
99
99
|
|
100
100
|
# Marks every message on the given queue for deletion.
|
101
101
|
def clear_queue(queue_name)
|
102
|
-
execute :delete, "#{queue_name}/messages", {}, :x_ms_version => '
|
102
|
+
execute :delete, "#{queue_name}/messages", {}, :x_ms_version => '2011-08-18'
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -68,7 +68,7 @@ module WAZ
|
|
68
68
|
# the canonicalized header line and the canonical form of the message, all of the joined by \n character. Encoded with
|
69
69
|
# Base64 and encrypted with SHA256 using the access_key as the seed.
|
70
70
|
def generate_signature(options = {})
|
71
|
-
return generate_signature20090919(options) if options[:headers]["x-ms-version"] == "
|
71
|
+
return generate_signature20090919(options) if options[:headers]["x-ms-version"] == "2011-08-18"
|
72
72
|
|
73
73
|
signature = options[:method].to_s.upcase + "\x0A" +
|
74
74
|
(options[:headers]["Content-MD5"] or "") + "\x0A" +
|
data/rakefile
CHANGED
@@ -1,46 +1,16 @@
|
|
1
|
-
|
2
|
-
require 'rubygems'
|
1
|
+
#!/usr/bin/env rake
|
3
2
|
require 'rspec/core/rake_task'
|
4
|
-
require 'rake/gempackagetask'
|
5
3
|
require 'rake/rdoctask'
|
6
|
-
require '
|
7
|
-
|
8
|
-
task :default => [:specs]
|
4
|
+
require 'bundler/gem_tasks'
|
9
5
|
|
10
6
|
RSpec::Core::RakeTask.new do |t|
|
11
7
|
t.name = :specs
|
12
|
-
t.pattern = "tests
|
8
|
+
t.pattern = "tests/**/*_test.rb"
|
13
9
|
t.rcov = true
|
14
10
|
t.rcov_opts = ['--text-report', '--exclude', "exclude.*/.gem,test,Library,#{ENV['GEM_HOME']}", '--sort', 'coverage' ]
|
15
11
|
t.rspec_opts = ['-cfn']
|
16
12
|
end
|
17
13
|
|
18
|
-
|
19
|
-
namespace :dist do
|
20
|
-
spec = Gem::Specification.new do |s|
|
21
|
-
s.name = 'waz-storage'
|
22
|
-
s.version = Gem::Version.new(WAZ::Storage::Version)
|
23
|
-
s.summary = "Client library for Windows Azure's Storage Service REST API"
|
24
|
-
s.description = "A simple implementation of Windows Azure Storage API for Ruby, inspired by the S3 gems and self experience of dealing with queues. The major goal of the whole gem is to enable ruby developers [like me =)] to leverage Windows Azure Storage features and have another option for cloud storage."
|
25
|
-
s.email = 'johnny.halife@me.com'
|
26
|
-
s.author = 'Johnny G. Halife'
|
27
|
-
s.homepage = 'http://waz-storage.heroku.com'
|
28
|
-
s.require_paths = ["lib"]
|
29
|
-
s.files = FileList['rakefile', 'lib/**/*.rb']
|
30
|
-
s.test_files = Dir['test/**/*']
|
31
|
-
s.has_rdoc = true
|
32
|
-
s.rdoc_options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
33
|
-
|
34
|
-
# Dependencies
|
35
|
-
s.add_dependency 'rest-client'
|
36
|
-
s.add_dependency 'ruby-hmac'
|
37
|
-
end
|
38
|
-
|
39
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
40
|
-
pkg.need_tar = true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
14
|
namespace :docs do
|
45
15
|
Rake::RDocTask.new do |t|
|
46
16
|
t.rdoc_dir = 'rdoc'
|
@@ -50,4 +20,4 @@ namespace :docs do
|
|
50
20
|
t.rdoc_files.include('README.rdoc')
|
51
21
|
t.rdoc_files.include('lib/**/*.rb')
|
52
22
|
end
|
53
|
-
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/autorun'
|
4
|
+
require 'mocha'
|
5
|
+
require 'restclient'
|
6
|
+
require 'time'
|
7
|
+
require 'hmac-sha2'
|
8
|
+
require 'base64'
|
9
|
+
require 'rexml/document'
|
10
|
+
require 'rexml/xpath'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.mock_with :mocha
|
14
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# enabling the load of files from root (on RSpec)
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../')
|
3
|
+
require 'tests/configuration'
|
4
|
+
require 'lib/waz-blobs'
|
5
|
+
|
6
|
+
describe "Windows Azure Blobs interface API" do
|
7
|
+
it "should return blob path from url" do
|
8
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
9
|
+
blob.path.should == "container/blob"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return blob metdata" do
|
13
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
14
|
+
WAZ::Blobs::Service.any_instance.expects(:get_blob_properties).with("container/blob").returns({:x_ms_meta_name => "blob_name"})
|
15
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
16
|
+
blob.metadata.should == { :x_ms_meta_name => "blob_name" }
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should put blob metadataa" do
|
20
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
21
|
+
WAZ::Blobs::Service.any_instance.expects(:set_blob_properties).with("container/blob", {:x_ms_meta_name => "blob_name"})
|
22
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
23
|
+
blob.put_properties!({ :x_ms_meta_name => "blob_name" })
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should get blob contents" do
|
27
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
28
|
+
WAZ::Blobs::Service.any_instance.expects(:get_blob).with("container/blob").returns("this is the blob content")
|
29
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
30
|
+
blob.value.should == "this is the blob content"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should put blob contents" do
|
34
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
35
|
+
WAZ::Blobs::Service.any_instance.expects(:get_blob_properties).with("container/blob").returns({})
|
36
|
+
WAZ::Blobs::Service.any_instance.expects(:put_blob).with("container/blob", "my new blob value", "application/xml", {}).returns("this is the blob content")
|
37
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
38
|
+
blob.value = "my new blob value"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should destroy blob" do
|
42
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
43
|
+
WAZ::Blobs::Service.any_instance.expects(:delete_blob).with("container/blob")
|
44
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "application/xml")
|
45
|
+
blob.destroy!
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should copy blob" do
|
49
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
50
|
+
WAZ::Blobs::BlobObject.service_instance.expects(:copy_blob).with('container/blob', 'container/blob-copy')
|
51
|
+
WAZ::Blobs::BlobObject.service_instance.expects(:get_blob_properties).with('container/blob-copy').returns(:content_type => "plain/text")
|
52
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "plain/text")
|
53
|
+
copy = blob.copy('container/blob-copy')
|
54
|
+
copy.path.should == "container/blob-copy"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should take a snapshot of a blob" do
|
58
|
+
mock_time = Time.new
|
59
|
+
Time.stubs(:new).returns(mock_time)
|
60
|
+
|
61
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
62
|
+
WAZ::Blobs::BlobObject.service_instance.expects(:get_blob_properties).with('container/blob').returns(:content_type => "plain/text")
|
63
|
+
WAZ::Blobs::BlobObject.service_instance.expects(:snapshot_blob).with('container/blob').returns(mock_time.httpdate)
|
64
|
+
|
65
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "plain/text")
|
66
|
+
blob_snapshot = blob.snapshot
|
67
|
+
blob_snapshot.snapshot_date.should === mock_time.httpdate
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should not allow snapshoted blobs to perform write operations" do
|
71
|
+
snapshot = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob", :content_type => "plain/text", :snapshot_date => Time.new.httpdate)
|
72
|
+
lambda { snapshot.value = "new-value" }.should raise_error(WAZ::Blobs::InvalidOperation)
|
73
|
+
lambda { snapshot.put_properties!({:x_ms_meta_name => "foo"}) }.should raise_error(WAZ::Blobs::InvalidOperation)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "blob path should include snapshot parameter" do
|
77
|
+
blob = WAZ::Blobs::BlobObject.new(:name => "blob_name", :url => "http://localhost/container/blob?snapshot=foo", :content_type => "application/xml")
|
78
|
+
blob.path.should == "container/blob?snapshot=foo"
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# enabling the load of files from root (on RSpec)
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../')
|
3
|
+
require 'tests/configuration'
|
4
|
+
require 'lib/waz-blobs'
|
5
|
+
|
6
|
+
describe "Windows Azure Containers interface API" do
|
7
|
+
|
8
|
+
it "should should throw when no container name is provided" do
|
9
|
+
lambda {WAZ::Blobs::Container.new}.should raise_error(WAZ::Storage::InvalidOption)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should list containers" do
|
13
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
14
|
+
WAZ::Blobs::Service.any_instance.expects(:list_containers).returns([{:name => 'my-container'}, {:name => 'other container'}])
|
15
|
+
containers = WAZ::Blobs::Container.list
|
16
|
+
containers.size.should == 2
|
17
|
+
containers.first().name.should == "my-container"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to create a container" do
|
21
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
22
|
+
WAZ::Blobs::Service.any_instance.expects(:create_container).with("my-container")
|
23
|
+
container = WAZ::Blobs::Container.create('my-container')
|
24
|
+
container.name.should == 'my-container'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be able to return a container by name" do
|
28
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
29
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:name => "container-name", :x_ms_meta_name => "container-name"}).twice
|
30
|
+
container = WAZ::Blobs::Container.find('container-name')
|
31
|
+
container.metadata[:x_ms_meta_name].should == 'container-name'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to return container metadata" do
|
35
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
36
|
+
WAZ::Blobs::Service.any_instance.expects(:create_container).with("container-name")
|
37
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
38
|
+
container = WAZ::Blobs::Container.create('container-name')
|
39
|
+
container.metadata[:x_ms_meta_name].should == 'container-name'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be able to say whether the container is public or not" do
|
43
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
44
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
45
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_acl).with("container-name").returns(false)
|
46
|
+
container = WAZ::Blobs::Container.find("container-name")
|
47
|
+
container.public_access?.should == false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be able to set whether the container is public or not" do
|
51
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
52
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
53
|
+
WAZ::Blobs::Service.any_instance.expects(:set_container_acl).with('container-name', false)
|
54
|
+
container = WAZ::Blobs::Container.find("container-name")
|
55
|
+
container.public_access = false
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should be able to set container properties" do
|
59
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
60
|
+
WAZ::Blobs::Service.any_instance.expects(:set_container_properties).with("container-name", {:x_ms_meta_meta1 => "meta1"}).returns(false)
|
61
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
62
|
+
container = WAZ::Blobs::Container.find("container-name")
|
63
|
+
container.put_properties!(:x_ms_meta_meta1 => "meta1")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be able to return a list files within the container" do
|
67
|
+
expected_blobs = [ {:name => 'blob1', :url => 'url', :content_type => 'app/xml'}, {:name => 'blob2', :url => 'url', :content_type => 'app/xml'} ]
|
68
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
69
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
70
|
+
WAZ::Blobs::Service.any_instance.expects(:list_blobs).with("container-name").returns(expected_blobs)
|
71
|
+
container = WAZ::Blobs::Container.find("container-name")
|
72
|
+
container_blobs = container.blobs
|
73
|
+
container_blobs.first().name = expected_blobs[0][:name]
|
74
|
+
container_blobs[1].name = expected_blobs[1][:name]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should destroy container" do
|
78
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
79
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
80
|
+
WAZ::Blobs::Service.any_instance.expects(:delete_container).with("container-name")
|
81
|
+
container = WAZ::Blobs::Container.find("container-name")
|
82
|
+
container.destroy!
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should be able to return null when container not found by name" do
|
86
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
87
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").raises(RestClient::ResourceNotFound)
|
88
|
+
container = WAZ::Blobs::Container.find('container-name')
|
89
|
+
container.nil?.should == true
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be able to put blob inside given container" do
|
93
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
94
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
95
|
+
WAZ::Blobs::Service.any_instance.expects(:put_blob).with("container-name/my_blob", "this is the blob content", "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
96
|
+
container = WAZ::Blobs::Container.find("container-name")
|
97
|
+
blob = container.store("my_blob", "this is the blob content", "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
98
|
+
blob.name.should == "my_blob"
|
99
|
+
blob.url.should == "http://my_account.blob.core.windows.net/container-name/my_blob"
|
100
|
+
blob.content_type.should == "text/plain; charset=UTF-8"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should be able to upload a stream to a blob inside a given container" do
|
104
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
105
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
106
|
+
WAZ::Blobs::Service.any_instance.expects(:put_block).with("container-name/my_blob", Base64.encode64('%064d' % 0), "this is the blob content")
|
107
|
+
WAZ::Blobs::Service.any_instance.expects(:put_block_list).with("container-name/my_blob", [Base64.encode64('%064d' % 0)], "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
108
|
+
container = WAZ::Blobs::Container.find("container-name")
|
109
|
+
blob = container.upload("my_blob", StringIO.new("this is the blob content"), "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
110
|
+
blob.name.should == "my_blob"
|
111
|
+
blob.url.should == "http://my_account.blob.core.windows.net/container-name/my_blob"
|
112
|
+
blob.content_type.should == "text/plain; charset=UTF-8"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should be able to put blob inside given container (when simulating fake containers)" do
|
116
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
117
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
118
|
+
WAZ::Blobs::Service.any_instance.expects(:put_blob).with("container-name/fake-container/blob", "this is the blob content", "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
119
|
+
container = WAZ::Blobs::Container.find("container-name")
|
120
|
+
blob = container.store("/fake-container/blob", "this is the blob content", "text/plain; charset=UTF-8", {:x_ms_meta_custom_property => "customValue"})
|
121
|
+
blob.name.should == "fake-container/blob"
|
122
|
+
blob.url.should == "http://my_account.blob.core.windows.net/container-name/fake-container/blob"
|
123
|
+
blob.content_type.should == "text/plain; charset=UTF-8"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should return a specific blob for the given container" do
|
127
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
128
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
129
|
+
WAZ::Blobs::Service.any_instance.expects(:get_blob_properties).with("container-name/my_blob").returns({ :content_type => "application/xml" })
|
130
|
+
container = WAZ::Blobs::Container.find("container-name")
|
131
|
+
blob = container['my_blob']
|
132
|
+
blob.name.should == 'my_blob'
|
133
|
+
blob.content_type.should == 'application/xml'
|
134
|
+
blob.url.should == 'http://my_account.blob.core.windows.net/container-name/my_blob'
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should return nil when the file does not exist" do
|
138
|
+
WAZ::Storage::Base.stubs(:default_connection).returns({:account_name => "my_account", :access_key => "key"})
|
139
|
+
WAZ::Blobs::Service.any_instance.expects(:get_container_properties).with("container-name").returns({:x_ms_meta_name => "container-name"})
|
140
|
+
WAZ::Blobs::Service.any_instance.expects(:get_blob_properties).with("container-name/my_blob").raises(RestClient::ResourceNotFound)
|
141
|
+
container = WAZ::Blobs::Container.find('container-name')
|
142
|
+
blob = container['my_blob']
|
143
|
+
blob.nil?.should == true
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should raise an exception when container name starts with - (hypen)" do
|
147
|
+
lambda { WAZ::Blobs::Container.create('-container') }.should raise_error(WAZ::Storage::InvalidParameterValue)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should raise an exception when container name ends with - (hypen)" do
|
151
|
+
lambda { WAZ::Blobs::Container.create('container-') }.should raise_error(WAZ::Storage::InvalidParameterValue)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should raise an exception when container name is less than 3" do
|
155
|
+
lambda { WAZ::Blobs::Container.create('co') }.should raise_error(WAZ::Storage::InvalidParameterValue)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should raise an exception when container name is longer than 63" do
|
159
|
+
lambda { WAZ::Blobs::Container.create('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') }.should raise_error(WAZ::Storage::InvalidParameterValue)
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|