waz-storage 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +9 -9
- data/CHANGELOG.rdoc +72 -72
- data/Gemfile +4 -4
- data/Gemfile.lock +46 -40
- data/LICENSE +18 -18
- data/README.rdoc +310 -310
- data/lib/waz-blobs.rb +4 -4
- data/lib/waz-queues.rb +6 -6
- data/lib/waz-storage.rb +39 -39
- data/lib/waz-tables.rb +4 -4
- data/lib/waz/blobs/blob_object.rb +122 -122
- data/lib/waz/blobs/container.rb +172 -161
- data/lib/waz/blobs/exceptions.rb +10 -10
- data/lib/waz/blobs/service.rb +181 -156
- data/lib/waz/queues/exceptions.rb +28 -28
- data/lib/waz/queues/message.rb +64 -64
- data/lib/waz/queues/queue.rb +164 -164
- data/lib/waz/queues/service.rb +105 -105
- data/lib/waz/storage/base.rb +70 -70
- data/lib/waz/storage/exceptions.rb +33 -33
- data/lib/waz/storage/validation_rules.rb +25 -25
- data/lib/waz/tables/edm_type_helper.rb +44 -44
- data/lib/waz/tables/exceptions.rb +44 -44
- data/lib/waz/tables/service.rb +178 -178
- data/lib/waz/tables/table.rb +74 -74
- data/lib/waz/tables/table_array.rb +10 -10
- data/rakefile +8 -21
- data/{tests → spec}/configuration.rb +22 -22
- data/{tests/waz/blobs/blob_object_test.rb → spec/waz/blobs/blob_object_spec.rb} +80 -80
- data/{tests/waz/blobs/container_test.rb → spec/waz/blobs/container_spec.rb} +175 -162
- data/{tests/waz/blobs/service_test.rb → spec/waz/blobs/service_spec.rb} +336 -282
- data/{tests/waz/queues/message_test.rb → spec/waz/queues/message_spec.rb} +32 -32
- data/{tests/waz/queues/queue_test.rb → spec/waz/queues/queue_spec.rb} +205 -205
- data/{tests/waz/queues/service_test.rb → spec/waz/queues/service_spec.rb} +298 -298
- data/{tests → spec}/waz/storage/base_tests.rb +81 -81
- data/{tests/waz/storage/shared_key_core_service_test.rb → spec/waz/storage/shared_key_core_service_spec.rb} +141 -141
- data/{tests/waz/tables/service_test.rb → spec/waz/tables/service_spec.rb} +613 -613
- data/{tests/waz/tables/table_test.rb → spec/waz/tables/table_spec.rb} +97 -97
- data/waz-storage.gemspec +29 -27
- metadata +47 -26
data/lib/waz/storage/base.rb
CHANGED
@@ -1,70 +1,70 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Storage
|
3
|
-
# This class is used to handle a general connection with Windows Azure Storage Account and it
|
4
|
-
# should be used at least once on the application bootstrap or configuration file.
|
5
|
-
#
|
6
|
-
# The usage is pretty simple as it's depicted on the following sample
|
7
|
-
# WAZ::Storage::establish_connection!(:account_name => "my_account_name",
|
8
|
-
# :access_key => "your_base64_key",
|
9
|
-
# :use_ssl => false)
|
10
|
-
#
|
11
|
-
class Base
|
12
|
-
class << self
|
13
|
-
# Sets the basic configuration parameters to use the API on the current context
|
14
|
-
# required parameters are :account_name, :access_key.
|
15
|
-
#
|
16
|
-
# All other parameters are optional.
|
17
|
-
def establish_connection!(options = {})
|
18
|
-
raise InvalidOption, :account_name unless options.keys.include? :account_name
|
19
|
-
raise InvalidOption, :access_key if !options.keys.include? :use_sas_auth_only unless options.keys.include? :access_key
|
20
|
-
raise InvalidOption, :use_sas_auth_only if !options.keys.include? :access_key unless options.keys.include? :use_sas_auth_only
|
21
|
-
raise InvalidOption, :sharedaccesssignature if !options.keys.include? :access_key unless options.keys.include? :sharedaccesssignature and options.keys.include? :use_sas_auth_only
|
22
|
-
options[:use_ssl] = false unless options.keys.include? :use_ssl
|
23
|
-
(@connections ||= []) << options
|
24
|
-
end
|
25
|
-
|
26
|
-
# Block Syntax
|
27
|
-
#
|
28
|
-
# Pushes the named repository onto the context-stack,
|
29
|
-
# yields a new session, and pops the context-stack.
|
30
|
-
#
|
31
|
-
# This helps you set contextual operations like in the following sample
|
32
|
-
#
|
33
|
-
# Base.establish_connection(options) do
|
34
|
-
# # do some operations on the given context
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# The context is restored to the previous one (what you configured on establish_connection!)
|
38
|
-
# or nil.
|
39
|
-
#
|
40
|
-
# Non-Block Syntax
|
41
|
-
#
|
42
|
-
# Behaves exactly as establish_connection!
|
43
|
-
def establish_connection(options = {}) # :yields: current_context
|
44
|
-
establish_connection!(options)
|
45
|
-
if (block_given?)
|
46
|
-
begin
|
47
|
-
return yield
|
48
|
-
ensure
|
49
|
-
@connections.pop() if connected?
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Returns the default connection (last set) parameters. It will raise an exception WAZ::Storage::NotConnected
|
55
|
-
# when there's no connection information registered.
|
56
|
-
def default_connection
|
57
|
-
raise NotConnected unless connected?
|
58
|
-
return @connections.last
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns a value indicating whether the current connection information has been set or not.
|
62
|
-
def connected?
|
63
|
-
return false if (@connections.nil?)
|
64
|
-
return false if (@connections.empty?)
|
65
|
-
return true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Storage
|
3
|
+
# This class is used to handle a general connection with Windows Azure Storage Account and it
|
4
|
+
# should be used at least once on the application bootstrap or configuration file.
|
5
|
+
#
|
6
|
+
# The usage is pretty simple as it's depicted on the following sample
|
7
|
+
# WAZ::Storage::establish_connection!(:account_name => "my_account_name",
|
8
|
+
# :access_key => "your_base64_key",
|
9
|
+
# :use_ssl => false)
|
10
|
+
#
|
11
|
+
class Base
|
12
|
+
class << self
|
13
|
+
# Sets the basic configuration parameters to use the API on the current context
|
14
|
+
# required parameters are :account_name, :access_key.
|
15
|
+
#
|
16
|
+
# All other parameters are optional.
|
17
|
+
def establish_connection!(options = {})
|
18
|
+
raise InvalidOption, :account_name unless options.keys.include? :account_name
|
19
|
+
raise InvalidOption, :access_key if !options.keys.include? :use_sas_auth_only unless options.keys.include? :access_key
|
20
|
+
raise InvalidOption, :use_sas_auth_only if !options.keys.include? :access_key unless options.keys.include? :use_sas_auth_only
|
21
|
+
raise InvalidOption, :sharedaccesssignature if !options.keys.include? :access_key unless options.keys.include? :sharedaccesssignature and options.keys.include? :use_sas_auth_only
|
22
|
+
options[:use_ssl] = false unless options.keys.include? :use_ssl
|
23
|
+
(@connections ||= []) << options
|
24
|
+
end
|
25
|
+
|
26
|
+
# Block Syntax
|
27
|
+
#
|
28
|
+
# Pushes the named repository onto the context-stack,
|
29
|
+
# yields a new session, and pops the context-stack.
|
30
|
+
#
|
31
|
+
# This helps you set contextual operations like in the following sample
|
32
|
+
#
|
33
|
+
# Base.establish_connection(options) do
|
34
|
+
# # do some operations on the given context
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# The context is restored to the previous one (what you configured on establish_connection!)
|
38
|
+
# or nil.
|
39
|
+
#
|
40
|
+
# Non-Block Syntax
|
41
|
+
#
|
42
|
+
# Behaves exactly as establish_connection!
|
43
|
+
def establish_connection(options = {}) # :yields: current_context
|
44
|
+
establish_connection!(options)
|
45
|
+
if (block_given?)
|
46
|
+
begin
|
47
|
+
return yield
|
48
|
+
ensure
|
49
|
+
@connections.pop() if connected?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the default connection (last set) parameters. It will raise an exception WAZ::Storage::NotConnected
|
55
|
+
# when there's no connection information registered.
|
56
|
+
def default_connection
|
57
|
+
raise NotConnected unless connected?
|
58
|
+
return @connections.last
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a value indicating whether the current connection information has been set or not.
|
62
|
+
def connected?
|
63
|
+
return false if (@connections.nil?)
|
64
|
+
return false if (@connections.empty?)
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,33 +1,33 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Storage
|
3
|
-
# This class is the base exception from where all the exceptions raised from this API
|
4
|
-
# inherit from. If you want to handle an exception that your code may throw and you don't
|
5
|
-
# know which specific type you should handle, handle this type of exception.
|
6
|
-
class StorageException < StandardError
|
7
|
-
end
|
8
|
-
|
9
|
-
# This exception raises whenever a required parameter for initializing any class isn't provided. From
|
10
|
-
# WAZ::Storage::Base up to WAZ::Queues::Queue all of them use this exception.
|
11
|
-
class InvalidOption < StorageException
|
12
|
-
def initialize(missing_option)
|
13
|
-
super("You did not provide one of the required parameters. Please provide the #{missing_option}.")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# This exception is raised when the user tries to perform an operation on any Storage API class
|
18
|
-
# without previously specificying the connection options.
|
19
|
-
class NotConnected < StorageException
|
20
|
-
def initialize
|
21
|
-
super("You should establish connection before using the services, the connection configuration is required.")
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# This exception if raised when the value given for an argument doesn't fall into the permitted values. For example
|
26
|
-
# if values on the blocklisttype aren't [all|uncommitted|committed]
|
27
|
-
class InvalidParameterValue < StorageException
|
28
|
-
def initialize(args = {})
|
29
|
-
super("The value supplied for the parameter #{args[:name]} is invalid. Permitted values are #{args[:values].join(',')}")
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Storage
|
3
|
+
# This class is the base exception from where all the exceptions raised from this API
|
4
|
+
# inherit from. If you want to handle an exception that your code may throw and you don't
|
5
|
+
# know which specific type you should handle, handle this type of exception.
|
6
|
+
class StorageException < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# This exception raises whenever a required parameter for initializing any class isn't provided. From
|
10
|
+
# WAZ::Storage::Base up to WAZ::Queues::Queue all of them use this exception.
|
11
|
+
class InvalidOption < StorageException
|
12
|
+
def initialize(missing_option)
|
13
|
+
super("You did not provide one of the required parameters. Please provide the #{missing_option}.")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# This exception is raised when the user tries to perform an operation on any Storage API class
|
18
|
+
# without previously specificying the connection options.
|
19
|
+
class NotConnected < StorageException
|
20
|
+
def initialize
|
21
|
+
super("You should establish connection before using the services, the connection configuration is required.")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This exception if raised when the value given for an argument doesn't fall into the permitted values. For example
|
26
|
+
# if values on the blocklisttype aren't [all|uncommitted|committed]
|
27
|
+
class InvalidParameterValue < StorageException
|
28
|
+
def initialize(args = {})
|
29
|
+
super("The value supplied for the parameter #{args[:name]} is invalid. Permitted values are #{args[:values].join(',')}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Storage
|
3
|
-
class ValidationRules
|
4
|
-
class << self
|
5
|
-
# Validates that the Container/Queue name given matches with the requirements of Windows Azure.
|
6
|
-
#
|
7
|
-
# -Container/Queue names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
|
8
|
-
# -Every dash (-) character must be immediately preceded and followed by a letter or number.
|
9
|
-
# -All letters in a container name must be lowercase.
|
10
|
-
# -Container/Queue names must be from 3 through 63 characters long.
|
11
|
-
def valid_name?(name)
|
12
|
-
name =~ /^[a-z0-9][a-z0-9\-]{1,}[^-]$/ && name.length < 64
|
13
|
-
end
|
14
|
-
|
15
|
-
# Validates that the Table name given matches with the requirements of Windows Azure.
|
16
|
-
#
|
17
|
-
# -Table names must start with at least one lower / upper character.
|
18
|
-
# -Table names can have character or any digit starting from the second position.
|
19
|
-
# -Table names must be from 3 through 63 characters long.
|
20
|
-
def valid_table_name?(name)
|
21
|
-
name =~ /^([a-z]|[A-Z]){1}([a-z]|[A-Z]|\d){2,62}$/
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Storage
|
3
|
+
class ValidationRules
|
4
|
+
class << self
|
5
|
+
# Validates that the Container/Queue name given matches with the requirements of Windows Azure.
|
6
|
+
#
|
7
|
+
# -Container/Queue names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
|
8
|
+
# -Every dash (-) character must be immediately preceded and followed by a letter or number.
|
9
|
+
# -All letters in a container name must be lowercase.
|
10
|
+
# -Container/Queue names must be from 3 through 63 characters long.
|
11
|
+
def valid_name?(name)
|
12
|
+
name =~ /^[a-z0-9][a-z0-9\-]{1,}[^-]$/ && name.length < 64
|
13
|
+
end
|
14
|
+
|
15
|
+
# Validates that the Table name given matches with the requirements of Windows Azure.
|
16
|
+
#
|
17
|
+
# -Table names must start with at least one lower / upper character.
|
18
|
+
# -Table names can have character or any digit starting from the second position.
|
19
|
+
# -Table names must be from 3 through 63 characters long.
|
20
|
+
def valid_table_name?(name)
|
21
|
+
name =~ /^([a-z]|[A-Z]){1}([a-z]|[A-Z]|\d){2,62}$/
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
26
|
end
|
@@ -1,45 +1,45 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Tables
|
3
|
-
class EdmTypeHelper
|
4
|
-
class << self
|
5
|
-
def parse_from(item)
|
6
|
-
return nil if !item.attributes['m:null'].nil? and item.attributes['m:null'] == 'true'
|
7
|
-
case item.attributes['m:type']
|
8
|
-
when 'Edm.Int16', 'Edm.Int32', 'Edm.Int64'
|
9
|
-
item.text.to_i
|
10
|
-
when 'Edm.Single', 'Edm.Double'
|
11
|
-
item.text.to_f
|
12
|
-
when 'Edm.Boolean'
|
13
|
-
item.text == 'true'
|
14
|
-
when 'Edm.DateTime'
|
15
|
-
Time.parse(item.text)
|
16
|
-
when 'Edm.Binary'
|
17
|
-
StringIO.new(Base64.decode64(item.text))
|
18
|
-
else
|
19
|
-
item.text
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def parse_to(item)
|
24
|
-
case item.class.name
|
25
|
-
when 'String'
|
26
|
-
[item, 'Edm.String']
|
27
|
-
when 'Fixnum'
|
28
|
-
[item, 'Edm.Int32']
|
29
|
-
when 'Float'
|
30
|
-
[item, 'Edm.Double']
|
31
|
-
when 'TrueClass', 'FalseClass'
|
32
|
-
[item, 'Edm.Boolean']
|
33
|
-
when 'Time'
|
34
|
-
[item.iso8601, 'Edm.DateTime']
|
35
|
-
when 'File', 'StringIO'
|
36
|
-
item.pos = 0
|
37
|
-
[Base64.encode64(item.read), 'Edm.Binary']
|
38
|
-
else
|
39
|
-
[item, 'Edm.String']
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Tables
|
3
|
+
class EdmTypeHelper
|
4
|
+
class << self
|
5
|
+
def parse_from(item)
|
6
|
+
return nil if !item.attributes['m:null'].nil? and item.attributes['m:null'] == 'true'
|
7
|
+
case item.attributes['m:type']
|
8
|
+
when 'Edm.Int16', 'Edm.Int32', 'Edm.Int64'
|
9
|
+
item.text.to_i
|
10
|
+
when 'Edm.Single', 'Edm.Double'
|
11
|
+
item.text.to_f
|
12
|
+
when 'Edm.Boolean'
|
13
|
+
item.text == 'true'
|
14
|
+
when 'Edm.DateTime'
|
15
|
+
Time.parse(item.text)
|
16
|
+
when 'Edm.Binary'
|
17
|
+
StringIO.new(Base64.decode64(item.text))
|
18
|
+
else
|
19
|
+
item.text
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_to(item)
|
24
|
+
case item.class.name
|
25
|
+
when 'String'
|
26
|
+
[item, 'Edm.String']
|
27
|
+
when 'Fixnum'
|
28
|
+
[item, 'Edm.Int32']
|
29
|
+
when 'Float'
|
30
|
+
[item, 'Edm.Double']
|
31
|
+
when 'TrueClass', 'FalseClass'
|
32
|
+
[item, 'Edm.Boolean']
|
33
|
+
when 'Time'
|
34
|
+
[item.iso8601, 'Edm.DateTime']
|
35
|
+
when 'File', 'StringIO'
|
36
|
+
item.pos = 0
|
37
|
+
[Base64.encode64(item.read), 'Edm.Binary']
|
38
|
+
else
|
39
|
+
[item, 'Edm.String']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
45
|
end
|
@@ -1,45 +1,45 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Tables
|
3
|
-
# This exception is raised while trying to create table that already exists.
|
4
|
-
class TableAlreadyExists < WAZ::Storage::StorageException
|
5
|
-
def initialize(name)
|
6
|
-
super("The table #{name} already exists on your account.")
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
# This exception is raised while trying to delete an unexisting table.
|
11
|
-
class TableDoesNotExist < WAZ::Storage::StorageException
|
12
|
-
def initialize(name)
|
13
|
-
super("The specified table #{name} does not exist.")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# This exception is raised when an invalid table name is provided.
|
18
|
-
class InvalidTableName < WAZ::Storage::StorageException
|
19
|
-
def initialize(name)
|
20
|
-
super("The table name #{name} is invalid, it must start with at least one lower/upper characted, must be from 3 through 63 characters long and can have character or any digit starting from the second position")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# This exception is raised when provided more than the 252 properties allowed by the Rest API.
|
25
|
-
class TooManyProperties < WAZ::Storage::StorageException
|
26
|
-
def initialize(total)
|
27
|
-
super("The entity contains more properties than allowed (252). The entity has #{total} properties.")
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# This exception is raised when the specified entity already exists.
|
32
|
-
class EntityAlreadyExists < WAZ::Storage::StorageException
|
33
|
-
def initialize(row_key)
|
34
|
-
super("The specified entity already exists. RowKey: #{row_key}")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# This exception is raised while trying to delete an unexisting entity.
|
39
|
-
class EntityDoesNotExist < WAZ::Storage::StorageException
|
40
|
-
def initialize(key)
|
41
|
-
super("The specified entity with #{key} does not exist.")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Tables
|
3
|
+
# This exception is raised while trying to create table that already exists.
|
4
|
+
class TableAlreadyExists < WAZ::Storage::StorageException
|
5
|
+
def initialize(name)
|
6
|
+
super("The table #{name} already exists on your account.")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# This exception is raised while trying to delete an unexisting table.
|
11
|
+
class TableDoesNotExist < WAZ::Storage::StorageException
|
12
|
+
def initialize(name)
|
13
|
+
super("The specified table #{name} does not exist.")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# This exception is raised when an invalid table name is provided.
|
18
|
+
class InvalidTableName < WAZ::Storage::StorageException
|
19
|
+
def initialize(name)
|
20
|
+
super("The table name #{name} is invalid, it must start with at least one lower/upper characted, must be from 3 through 63 characters long and can have character or any digit starting from the second position")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# This exception is raised when provided more than the 252 properties allowed by the Rest API.
|
25
|
+
class TooManyProperties < WAZ::Storage::StorageException
|
26
|
+
def initialize(total)
|
27
|
+
super("The entity contains more properties than allowed (252). The entity has #{total} properties.")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# This exception is raised when the specified entity already exists.
|
32
|
+
class EntityAlreadyExists < WAZ::Storage::StorageException
|
33
|
+
def initialize(row_key)
|
34
|
+
super("The specified entity already exists. RowKey: #{row_key}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# This exception is raised while trying to delete an unexisting entity.
|
39
|
+
class EntityDoesNotExist < WAZ::Storage::StorageException
|
40
|
+
def initialize(key)
|
41
|
+
super("The specified entity with #{key} does not exist.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
45
|
end
|
data/lib/waz/tables/service.rb
CHANGED
@@ -1,178 +1,178 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Tables
|
3
|
-
# This is internally used by the waz-tables part of the gem and it exposes the Windows Azure Blob API REST methods
|
4
|
-
# implementation. You can use this class to perform an specific operation that isn't provided by the current API.
|
5
|
-
class Service
|
6
|
-
include WAZ::Storage::SharedKeyCoreService
|
7
|
-
|
8
|
-
DATASERVICES_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices"
|
9
|
-
DATASERVICES_METADATA_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
10
|
-
|
11
|
-
# Creates a table on the current Windows Azure Storage account.
|
12
|
-
def create_table(table_name)
|
13
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
14
|
-
|
15
|
-
payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
|
16
|
-
"<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
|
17
|
-
"<title /><updated>#{Time.now.utc.iso8601}</updated><author><name/></author><id/>" \
|
18
|
-
"<content type=\"application/xml\"><m:properties><d:TableName>#{table_name}</d:TableName></m:properties></content></entry>"
|
19
|
-
|
20
|
-
begin
|
21
|
-
execute :post, 'Tables', {}, default_headers, payload
|
22
|
-
return {:name => table_name, :url => "#{self.base_url}/Tables('#{table_name}"}
|
23
|
-
rescue RestClient::RequestFailed
|
24
|
-
raise WAZ::Tables::TableAlreadyExists, table_name if $!.http_code == 409
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Delete a table on the current Windows Azure Storage account.
|
29
|
-
def delete_table(table_name)
|
30
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
31
|
-
begin
|
32
|
-
execute :delete, "Tables('#{table_name}')", {}, default_headers
|
33
|
-
rescue RestClient::ResourceNotFound
|
34
|
-
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Lists all existing tables on the current storage account.
|
39
|
-
# remove Content-Type if it's not working
|
40
|
-
def list_tables(next_table_name = nil)
|
41
|
-
query = { 'NextTableName' => next_table_name } unless next_table_name.nil?
|
42
|
-
content = execute :get, "Tables", query ||= {}, default_headers
|
43
|
-
|
44
|
-
doc = REXML::Document.new(content)
|
45
|
-
tables = REXML::XPath.each(doc, '/feed/entry').map do |item|
|
46
|
-
{ :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
|
47
|
-
:url => REXML::XPath.first(item, "id").text }
|
48
|
-
end
|
49
|
-
|
50
|
-
return tables, content.headers[:x_ms_continuation_nexttablename]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Retrieves an existing table on the current storage account.
|
54
|
-
# remove Content-Type if it's not working
|
55
|
-
def get_table(table_name)
|
56
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
57
|
-
|
58
|
-
begin
|
59
|
-
content = execute :get, "Tables('#{table_name}')", {}, default_headers
|
60
|
-
doc = REXML::Document.new(content)
|
61
|
-
item = REXML::XPath.first(doc, "entry")
|
62
|
-
return { :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
|
63
|
-
:url => REXML::XPath.first(item, "id").text }
|
64
|
-
rescue RestClient::ResourceNotFound
|
65
|
-
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Insert a new entity on the provided table for the current storage account
|
70
|
-
# TODO: catch all api errors as described on Table Service Error Codes on MSDN (http://msdn.microsoft.com/en-us/library/dd179438.aspx)
|
71
|
-
def insert_entity(table_name, entity)
|
72
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
73
|
-
raise WAZ::Tables::TooManyProperties, entity.length if entity.length > 252
|
74
|
-
|
75
|
-
begin
|
76
|
-
response = execute(:post, table_name, {}, default_headers, generate_payload(table_name, entity))
|
77
|
-
return parse_response(response)
|
78
|
-
rescue RestClient::RequestFailed
|
79
|
-
raise WAZ::Tables::EntityAlreadyExists, entity[:row_key] if $!.http_code == 409 and $!.response.body.include?('EntityAlreadyExists')
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Update an existing entity on the current storage account.
|
84
|
-
# TODO: handle specific errors
|
85
|
-
def update_entity(table_name, entity)
|
86
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
87
|
-
response = execute(:put, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}) , generate_payload(table_name, entity))
|
88
|
-
return parse_response(response)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Merge an existing entity on the current storage account.
|
92
|
-
# The Merge Entity operation updates an existing entity by updating the entity's properties.
|
93
|
-
# This operation does not replace the existing entity, as the Update Entity operation does
|
94
|
-
# TODO: handle specific errors
|
95
|
-
def merge_entity(table_name, entity)
|
96
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
97
|
-
response = execute(:merge, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}), generate_payload(table_name, entity))
|
98
|
-
return parse_response(response)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Delete an existing entity in a table.
|
102
|
-
def delete_entity(table_name, partition_key, row_key)
|
103
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
104
|
-
|
105
|
-
begin
|
106
|
-
execute :delete, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers.merge({'If-Match' => '*'})
|
107
|
-
rescue RestClient::ResourceNotFound
|
108
|
-
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404 and $!.response.body.include?('TableNotFound')
|
109
|
-
raise WAZ::Tables::EntityDoesNotExist, "(PartitionKey='#{partition_key}',RowKey='#{row_key}')" if $!.http_code == 404
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Retrieves an existing entity on the current storage account.
|
114
|
-
# TODO: handle specific errors
|
115
|
-
def get_entity(table_name, partition_key, row_key)
|
116
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
117
|
-
response = execute(:get, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers)
|
118
|
-
return parse_response(response)
|
119
|
-
end
|
120
|
-
|
121
|
-
# Retrieves a set of entities on the current storage account for a given query.
|
122
|
-
# When the :top => n is passed it returns only the first n rows that match with the query
|
123
|
-
# Optional parameters:
|
124
|
-
# * :headers a hash containing the request headers
|
125
|
-
# * :expression the filter query that will be executed against the table (see http://msdn.microsoft.com/en-us/library/dd179421.aspx for more information),
|
126
|
-
# * :top limits the amount of fields for this query.
|
127
|
-
# * :continuation_token the hash obtained when you perform a query that has more than 1000 records or exceeds the allowed timeout (see http://msdn.microsoft.com/en-us/library/dd135718.aspx)
|
128
|
-
def query(table_name, options = {})
|
129
|
-
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
130
|
-
query = {'$filter' => (options[:expression] or '') }
|
131
|
-
query.merge!({ '$top' => options[:top] }) unless options[:top].nil?
|
132
|
-
query.merge!(options[:continuation_token]) unless options[:continuation_token].nil?
|
133
|
-
response = execute :get, "#{table_name}()", query, default_headers
|
134
|
-
continuation_token = {'NextPartitionKey' => response.headers[:x_ms_continuation_nextpartitionkey], 'NextRowKey' => response.headers[:x_ms_continuation_nextrowkey]}
|
135
|
-
parse_response(response, continuation_token)
|
136
|
-
end
|
137
|
-
|
138
|
-
private
|
139
|
-
def generate_payload(table_name, entity)
|
140
|
-
payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
|
141
|
-
"<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
|
142
|
-
"<id>#{generate_request_uri "#{table_name}"}(PartitionKey='#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}',RowKey='#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}')</id>" \
|
143
|
-
"<title /><updated>#{Time.now.utc.iso8601}</updated><author><name /></author><link rel=\"edit\" title=\"#{table_name}\" href=\"#{table_name}(PartitionKey='#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}',RowKey='#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}')\" />" \
|
144
|
-
"<content type=\"application/xml\"><m:properties>"
|
145
|
-
|
146
|
-
entity.sort_by { |k| k.to_s }.each do |k,v|
|
147
|
-
value, type = EdmTypeHelper.parse_to(v)[0].to_s, EdmTypeHelper.parse_to(v)[1].to_s
|
148
|
-
payload << (!v.nil? ? "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\">#{REXML::Text.new(value, false, nil, false).to_s}</d:#{k.to_s}>" : "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\" m:null=\"true\" />") unless k.eql?(:partition_key) or k.eql?(:row_key)
|
149
|
-
end
|
150
|
-
|
151
|
-
payload << "<d:PartitionKey>#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}</d:PartitionKey>" \
|
152
|
-
"<d:RowKey>#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}</d:RowKey>" \
|
153
|
-
"</m:properties></content></entry>"
|
154
|
-
return payload
|
155
|
-
end
|
156
|
-
|
157
|
-
def parse_response(response, continuation_token = nil)
|
158
|
-
doc = REXML::Document.new(response)
|
159
|
-
entities = REXML::XPath.each(doc, '//entry').map do |entry|
|
160
|
-
fields = REXML::XPath.each(entry.elements['content'], 'm:properties/*', {"m" => DATASERVICES_METADATA_NAMESPACE}).map do |f|
|
161
|
-
{ f.name.gsub(/PartitionKey/i, 'partition_key').gsub(/RowKey/i, 'row_key').to_sym => EdmTypeHelper.parse_from(f) }
|
162
|
-
end
|
163
|
-
Hash[*fields.collect {|h| h.to_a}.flatten]
|
164
|
-
end
|
165
|
-
entities = WAZ::Tables::TableArray.new(entities)
|
166
|
-
entities.continuation_token = continuation_token
|
167
|
-
return (REXML::XPath.first(doc, '/feed')) ? entities : entities.first
|
168
|
-
end
|
169
|
-
|
170
|
-
def default_headers
|
171
|
-
{ 'Date' => Time.new.httpdate,
|
172
|
-
'Content-Type' => 'application/atom+xml',
|
173
|
-
'DataServiceVersion' => '1.0;NetFx',
|
174
|
-
'MaxDataServiceVersion' => '1.0;NetFx' }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Tables
|
3
|
+
# This is internally used by the waz-tables part of the gem and it exposes the Windows Azure Blob API REST methods
|
4
|
+
# implementation. You can use this class to perform an specific operation that isn't provided by the current API.
|
5
|
+
class Service
|
6
|
+
include WAZ::Storage::SharedKeyCoreService
|
7
|
+
|
8
|
+
DATASERVICES_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices"
|
9
|
+
DATASERVICES_METADATA_NAMESPACE = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
10
|
+
|
11
|
+
# Creates a table on the current Windows Azure Storage account.
|
12
|
+
def create_table(table_name)
|
13
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
14
|
+
|
15
|
+
payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
|
16
|
+
"<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
|
17
|
+
"<title /><updated>#{Time.now.utc.iso8601}</updated><author><name/></author><id/>" \
|
18
|
+
"<content type=\"application/xml\"><m:properties><d:TableName>#{table_name}</d:TableName></m:properties></content></entry>"
|
19
|
+
|
20
|
+
begin
|
21
|
+
execute :post, 'Tables', {}, default_headers, payload
|
22
|
+
return {:name => table_name, :url => "#{self.base_url}/Tables('#{table_name}"}
|
23
|
+
rescue RestClient::RequestFailed
|
24
|
+
raise WAZ::Tables::TableAlreadyExists, table_name if $!.http_code == 409
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Delete a table on the current Windows Azure Storage account.
|
29
|
+
def delete_table(table_name)
|
30
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
31
|
+
begin
|
32
|
+
execute :delete, "Tables('#{table_name}')", {}, default_headers
|
33
|
+
rescue RestClient::ResourceNotFound
|
34
|
+
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Lists all existing tables on the current storage account.
|
39
|
+
# remove Content-Type if it's not working
|
40
|
+
def list_tables(next_table_name = nil)
|
41
|
+
query = { 'NextTableName' => next_table_name } unless next_table_name.nil?
|
42
|
+
content = execute :get, "Tables", query ||= {}, default_headers
|
43
|
+
|
44
|
+
doc = REXML::Document.new(content)
|
45
|
+
tables = REXML::XPath.each(doc, '/feed/entry').map do |item|
|
46
|
+
{ :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
|
47
|
+
:url => REXML::XPath.first(item, "id").text }
|
48
|
+
end
|
49
|
+
|
50
|
+
return tables, content.headers[:x_ms_continuation_nexttablename]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieves an existing table on the current storage account.
|
54
|
+
# remove Content-Type if it's not working
|
55
|
+
def get_table(table_name)
|
56
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
57
|
+
|
58
|
+
begin
|
59
|
+
content = execute :get, "Tables('#{table_name}')", {}, default_headers
|
60
|
+
doc = REXML::Document.new(content)
|
61
|
+
item = REXML::XPath.first(doc, "entry")
|
62
|
+
return { :name => REXML::XPath.first(item.elements['content'], "m:properties/d:TableName", {"m" => DATASERVICES_METADATA_NAMESPACE, "d" => DATASERVICES_NAMESPACE}).text,
|
63
|
+
:url => REXML::XPath.first(item, "id").text }
|
64
|
+
rescue RestClient::ResourceNotFound
|
65
|
+
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Insert a new entity on the provided table for the current storage account
|
70
|
+
# TODO: catch all api errors as described on Table Service Error Codes on MSDN (http://msdn.microsoft.com/en-us/library/dd179438.aspx)
|
71
|
+
def insert_entity(table_name, entity)
|
72
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
73
|
+
raise WAZ::Tables::TooManyProperties, entity.length if entity.length > 252
|
74
|
+
|
75
|
+
begin
|
76
|
+
response = execute(:post, table_name, {}, default_headers, generate_payload(table_name, entity))
|
77
|
+
return parse_response(response)
|
78
|
+
rescue RestClient::RequestFailed
|
79
|
+
raise WAZ::Tables::EntityAlreadyExists, entity[:row_key] if $!.http_code == 409 and $!.response.body.include?('EntityAlreadyExists')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Update an existing entity on the current storage account.
|
84
|
+
# TODO: handle specific errors
|
85
|
+
def update_entity(table_name, entity)
|
86
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
87
|
+
response = execute(:put, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}) , generate_payload(table_name, entity))
|
88
|
+
return parse_response(response)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Merge an existing entity on the current storage account.
|
92
|
+
# The Merge Entity operation updates an existing entity by updating the entity's properties.
|
93
|
+
# This operation does not replace the existing entity, as the Update Entity operation does
|
94
|
+
# TODO: handle specific errors
|
95
|
+
def merge_entity(table_name, entity)
|
96
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
97
|
+
response = execute(:merge, "#{table_name}(PartitionKey='#{entity[:partition_key]}',RowKey='#{entity[:row_key]}')", {}, default_headers.merge({'If-Match' => '*'}), generate_payload(table_name, entity))
|
98
|
+
return parse_response(response)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Delete an existing entity in a table.
|
102
|
+
def delete_entity(table_name, partition_key, row_key)
|
103
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
104
|
+
|
105
|
+
begin
|
106
|
+
execute :delete, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers.merge({'If-Match' => '*'})
|
107
|
+
rescue RestClient::ResourceNotFound
|
108
|
+
raise WAZ::Tables::TableDoesNotExist, table_name if $!.http_code == 404 and $!.response.body.include?('TableNotFound')
|
109
|
+
raise WAZ::Tables::EntityDoesNotExist, "(PartitionKey='#{partition_key}',RowKey='#{row_key}')" if $!.http_code == 404
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Retrieves an existing entity on the current storage account.
|
114
|
+
# TODO: handle specific errors
|
115
|
+
def get_entity(table_name, partition_key, row_key)
|
116
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
117
|
+
response = execute(:get, "#{table_name}(PartitionKey='#{partition_key}',RowKey='#{row_key}')", {}, default_headers)
|
118
|
+
return parse_response(response)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Retrieves a set of entities on the current storage account for a given query.
|
122
|
+
# When the :top => n is passed it returns only the first n rows that match with the query
|
123
|
+
# Optional parameters:
|
124
|
+
# * :headers a hash containing the request headers
|
125
|
+
# * :expression the filter query that will be executed against the table (see http://msdn.microsoft.com/en-us/library/dd179421.aspx for more information),
|
126
|
+
# * :top limits the amount of fields for this query.
|
127
|
+
# * :continuation_token the hash obtained when you perform a query that has more than 1000 records or exceeds the allowed timeout (see http://msdn.microsoft.com/en-us/library/dd135718.aspx)
|
128
|
+
def query(table_name, options = {})
|
129
|
+
raise WAZ::Tables::InvalidTableName, table_name unless WAZ::Storage::ValidationRules.valid_table_name?(table_name)
|
130
|
+
query = {'$filter' => (options[:expression] or '') }
|
131
|
+
query.merge!({ '$top' => options[:top] }) unless options[:top].nil?
|
132
|
+
query.merge!(options[:continuation_token]) unless options[:continuation_token].nil?
|
133
|
+
response = execute :get, "#{table_name}()", query, default_headers
|
134
|
+
continuation_token = {'NextPartitionKey' => response.headers[:x_ms_continuation_nextpartitionkey], 'NextRowKey' => response.headers[:x_ms_continuation_nextrowkey]}
|
135
|
+
parse_response(response, continuation_token)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def generate_payload(table_name, entity)
|
140
|
+
payload = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" \
|
141
|
+
"<entry xmlns:d=\"#{DATASERVICES_NAMESPACE}\" xmlns:m=\"#{DATASERVICES_METADATA_NAMESPACE}\" xmlns=\"http://www.w3.org/2005/Atom\">" \
|
142
|
+
"<id>#{generate_request_uri "#{table_name}"}(PartitionKey='#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}',RowKey='#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}')</id>" \
|
143
|
+
"<title /><updated>#{Time.now.utc.iso8601}</updated><author><name /></author><link rel=\"edit\" title=\"#{table_name}\" href=\"#{table_name}(PartitionKey='#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}',RowKey='#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}')\" />" \
|
144
|
+
"<content type=\"application/xml\"><m:properties>"
|
145
|
+
|
146
|
+
entity.sort_by { |k| k.to_s }.each do |k,v|
|
147
|
+
value, type = EdmTypeHelper.parse_to(v)[0].to_s, EdmTypeHelper.parse_to(v)[1].to_s
|
148
|
+
payload << (!v.nil? ? "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\">#{REXML::Text.new(value, false, nil, false).to_s}</d:#{k.to_s}>" : "<d:#{k.to_s} m:type=\"#{k.edm_type || type}\" m:null=\"true\" />") unless k.eql?(:partition_key) or k.eql?(:row_key)
|
149
|
+
end
|
150
|
+
|
151
|
+
payload << "<d:PartitionKey>#{REXML::Text.new(entity[:partition_key], false, nil, false).to_s}</d:PartitionKey>" \
|
152
|
+
"<d:RowKey>#{REXML::Text.new(entity[:row_key], false, nil, false).to_s}</d:RowKey>" \
|
153
|
+
"</m:properties></content></entry>"
|
154
|
+
return payload
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_response(response, continuation_token = nil)
|
158
|
+
doc = REXML::Document.new(response)
|
159
|
+
entities = REXML::XPath.each(doc, '//entry').map do |entry|
|
160
|
+
fields = REXML::XPath.each(entry.elements['content'], 'm:properties/*', {"m" => DATASERVICES_METADATA_NAMESPACE}).map do |f|
|
161
|
+
{ f.name.gsub(/PartitionKey/i, 'partition_key').gsub(/RowKey/i, 'row_key').to_sym => EdmTypeHelper.parse_from(f) }
|
162
|
+
end
|
163
|
+
Hash[*fields.collect {|h| h.to_a}.flatten]
|
164
|
+
end
|
165
|
+
entities = WAZ::Tables::TableArray.new(entities)
|
166
|
+
entities.continuation_token = continuation_token
|
167
|
+
return (REXML::XPath.first(doc, '/feed')) ? entities : entities.first
|
168
|
+
end
|
169
|
+
|
170
|
+
def default_headers
|
171
|
+
{ 'Date' => Time.new.httpdate,
|
172
|
+
'Content-Type' => 'application/atom+xml',
|
173
|
+
'DataServiceVersion' => '1.0;NetFx',
|
174
|
+
'MaxDataServiceVersion' => '1.0;NetFx' }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|