azure-storage-table 1.0.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.
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+ require "delegate"
27
+
28
+ module Azure::Storage
29
+ module Table
30
+ # Public: Wrapper around a string to represent a GUID
31
+ #
32
+ class GUID < SimpleDelegator
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,118 @@
1
+ #-------------------------------------------------------------------------
2
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
3
+ #
4
+ # The MIT License(MIT)
5
+
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files(the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions :
12
+
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+ #--------------------------------------------------------------------------
24
+ require "azure/storage/table/table_service"
25
+
26
+ module Azure::Storage
27
+ module Table
28
+ class Query
29
+ def initialize(table = "", partition = nil, row = nil, &block)
30
+ @table = table
31
+ @partition_key = partition
32
+ @row_key = row
33
+ @fields = []
34
+ @filters = []
35
+ @top_n = nil
36
+ @table_service = Azure::Storage::Table::TableService.create_from_env
37
+ self.instance_eval(&block) if block_given?
38
+ end
39
+
40
+ attr_reader :table
41
+ attr_reader :partition_key
42
+ attr_reader :row_key
43
+
44
+ attr_reader :fields
45
+ attr_reader :filters
46
+ attr_reader :top_n
47
+
48
+ attr_reader :next_partition_key
49
+ attr_reader :next_row_key
50
+
51
+ attr_reader :table_service
52
+
53
+ def from(table_name)
54
+ @table = table_name
55
+ self
56
+ end
57
+
58
+ def partition(partition_key)
59
+ @partition_key = partition_key
60
+ self
61
+ end
62
+
63
+ def row(row_key)
64
+ @row_key = row_key
65
+ self
66
+ end
67
+
68
+ def select(*p)
69
+ @fields.concat(p)
70
+ self
71
+ end
72
+
73
+ def where(*p)
74
+ @filters.push(p)
75
+ self
76
+ end
77
+
78
+ def top(n)
79
+ @top_n = n
80
+ self
81
+ end
82
+
83
+ def next_partition(next_partition_key)
84
+ @next_partition_key = next_partition_key
85
+ self
86
+ end
87
+
88
+ def next_row(next_row_key)
89
+ @next_row_key = next_row_key
90
+ self
91
+ end
92
+
93
+ def execute
94
+ @table_service.query_entities(@table, partition_key: @partition_key,
95
+ row_key: @row_key,
96
+ select: @fields.map { |f| f.to_s },
97
+ filter: _build_filter_string,
98
+ top: (@top_n ? @top_n.to_i : @top_n),
99
+ continuation_token: {
100
+ next_partition_key: @next_partition_key,
101
+ next_row_key: @next_row_key
102
+ })
103
+ end
104
+
105
+ def _build_filter_string
106
+ result = ""
107
+ clauses = []
108
+ filters.each { |f|
109
+ clauses.push "#{f[0]} #{f[1]} #{Azure::Storage::Table::EdmType.serialize_query_value(f[2])}"
110
+ }
111
+ return nil if clauses.length == 0
112
+
113
+ result << clauses.join(" and ")
114
+ result
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+ require "time"
27
+ require "date"
28
+ require "json"
29
+
30
+ module Azure
31
+ module Storage
32
+ module Table
33
+ # This is a class that serializes the request/response body for table
34
+ # service.
35
+ module Serialization
36
+ include Azure::Storage::Common::Service::Serialization
37
+
38
+ def self.hash_to_json(h)
39
+ newhash = {}
40
+ h.map do |key, val|
41
+ type = Table::EdmType.property_type(val)
42
+ type_key = key.is_a?(Symbol) ? key.to_s + TableConstants::ODATA_TYPE_SUFFIX : key + TableConstants::ODATA_TYPE_SUFFIX
43
+ newhash[key] = EdmType::serialize_value(type, val)
44
+ newhash[type_key] = type unless type.nil? || type.empty? || h.key?(type_key)
45
+ end
46
+ JSON newhash
47
+ end
48
+
49
+ def self.table_entries_from_json(json)
50
+ table_entries_from_hash(hash_from_json(json))
51
+ end
52
+
53
+ def self.hash_from_json(json)
54
+ JSON.parse(json)
55
+ end
56
+
57
+ def self.table_entries_from_hash(h)
58
+ values = []
59
+ if h["value"]
60
+ h["value"].each do |name|
61
+ values.push(name)
62
+ end
63
+ elsif h["TableName"]
64
+ values = h
65
+ end
66
+ values
67
+ end
68
+
69
+ def self.entity_from_json(json)
70
+ entity_from_hash(hash_from_json(json))
71
+ end
72
+
73
+ def self.entities_from_json(json)
74
+ entities_hash = hash_from_json(json)
75
+ entities_hash["value"].nil? ? [entity_from_hash(entities_hash)] : entities_from_hash(entities_hash)
76
+ end
77
+
78
+ def self.entities_from_hash(h)
79
+ entities = []
80
+ h["value"].each { |entity_hash|
81
+ entities.push(entity_from_hash(entity_hash))
82
+ }
83
+ entities
84
+ end
85
+
86
+ def self.entity_from_hash(h)
87
+ Entity.new do |entity|
88
+ entity.etag = h.delete(TableConstants::ODATA_ETAG)
89
+ properties = {}
90
+ h.each do |k, v|
91
+ next if k.end_with? TableConstants::ODATA_TYPE_SUFFIX
92
+ type = h[k + TableConstants::ODATA_TYPE_SUFFIX]
93
+ properties[k] = EdmType::deserialize_value(v, type.nil? ? EdmType::property_type(v) : type)
94
+ end
95
+ entity.properties = properties
96
+ end
97
+ end
98
+
99
+ def self.get_accept_string(accept_type = :min_meta)
100
+ case accept_type
101
+ when :no_meta
102
+ Azure::Storage::Common::HeaderConstants::ODATA_NO_META
103
+ when :min_meta
104
+ Azure::Storage::Common::HeaderConstants::ODATA_MIN_META
105
+ when :full_meta
106
+ Azure::Storage::Common::HeaderConstants::ODATA_FULL_META
107
+ when nil
108
+ Azure::Storage::Common::HeaderConstants::ODATA_MIN_META
109
+ else
110
+ accept_type
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,775 @@
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+ require "azure/storage/table/auth/shared_key"
27
+
28
+ module Azure::Storage
29
+ include Azure::Storage::Common::Service
30
+ StorageService = Azure::Storage::Common::Service::StorageService
31
+
32
+ module Table
33
+ class TableService < StorageService
34
+ class << self
35
+ # Public: Creates an instance of [Azure::Storage::Table::TableService]
36
+ #
37
+ # ==== Attributes
38
+ #
39
+ # * +options+ - Hash. Optional parameters.
40
+ #
41
+ # ==== Options
42
+ #
43
+ # Accepted key/value pairs in options parameter are:
44
+ #
45
+ # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
46
+ # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
47
+ # * +:storage_account_name+ - String. The name of the storage account.
48
+ # * +:storage_access_key+ - Base64 String. The access key of the storage account.
49
+ # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
50
+ # * +:storage_table_host+ - String. Specified Table service endpoint or hostname
51
+ # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Service, to
52
+ # * +:default_endpoints_protocol+ - String. http or https
53
+ # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
54
+ # * +:ca_file+ - String. File path of the CA file if having issue with SSL
55
+ # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
56
+ #
57
+ # The valid set of options include:
58
+ # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
59
+ # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
60
+ # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
61
+ # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
62
+ # * Anonymous Table: only +:storage_table_host+, if it is to only access tables within a container
63
+ #
64
+ # Additional notes:
65
+ # * Specified hosts can be set when use account name with access key or sas token
66
+ # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
67
+ # * Storage emulator always use path style URI
68
+ # * +:ca_file+ is independent.
69
+ #
70
+ # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
71
+ #
72
+ # @return [Azure::Storage::Table::TableService]
73
+ def create(options = {}, &block)
74
+ service_options = { client: Azure::Storage::Common::Client::create(options, &block), api_version: Azure::Storage::Table::Default::STG_VERSION }
75
+ service_options[:user_agent_prefix] = options[:user_agent_prefix] if options[:user_agent_prefix]
76
+ Azure::Storage::Table::TableService.new(service_options, &block)
77
+ end
78
+
79
+ # Public: Creates an instance of [Azure::Storage::Table::TableService] with Storage Emulator
80
+ #
81
+ # ==== Attributes
82
+ #
83
+ # * +proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
84
+ #
85
+ # @return [Azure::Storage::Table::TableService]
86
+ def create_development(proxy_uri = nil, &block)
87
+ service_options = { client: Azure::Storage::Common::Client::create_development(proxy_uri, &block), api_version: Azure::Storage::Table::Default::STG_VERSION }
88
+ Azure::Storage::Table::TableService.new(service_options, &block)
89
+ end
90
+
91
+ # Public: Creates an instance of [Azure::Storage::Table::TableService] from Environment Variables
92
+ #
93
+ # @return [Azure::Storage::Table::TableService]
94
+ def create_from_env(&block)
95
+ service_options = { client: Azure::Storage::Common::Client::create_from_env(&block), api_version: Azure::Storage::Table::Default::STG_VERSION }
96
+ Azure::Storage::Table::TableService.new(service_options, &block)
97
+ end
98
+
99
+ # Public: Creates an instance of [Azure::Storage::Table::TableService] from Environment Variables
100
+ #
101
+ # ==== Attributes
102
+ #
103
+ # * +connection_string+ - String. Please refer to https://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/.
104
+ #
105
+ # @return [Azure::Storage::Table::TableService]
106
+ def create_from_connection_string(connection_string, &block)
107
+ service_options = { client: Azure::Storage::Common::Client::create_from_connection_string(connection_string, &block), api_version: Azure::Storage::Table::Default::STG_VERSION }
108
+ Azure::Storage::Table::TableService.new(service_options, &block)
109
+ end
110
+ end
111
+
112
+ # Public: Initializes an instance of [Azure::Storage::Table::TableService]
113
+ #
114
+ # ==== Attributes
115
+ #
116
+ # * +options+ - Hash. Optional parameters.
117
+ #
118
+ # ==== Options
119
+ #
120
+ # Accepted key/value pairs in options parameter are:
121
+ #
122
+ # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
123
+ # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
124
+ # * +:storage_connection_string+ - String. The storage connection string.
125
+ # * +:storage_account_name+ - String. The name of the storage account.
126
+ # * +:storage_access_key+ - Base64 String. The access key of the storage account.
127
+ # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
128
+ # * +:storage_table_host+ - String. Specified Table serivce endpoint or hostname
129
+ # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Serivce, to
130
+ # * +:default_endpoints_protocol+ - String. http or https
131
+ # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
132
+ # * +:ca_file+ - String. File path of the CA file if having issue with SSL
133
+ # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
134
+ # * +:client+ - Azure::Storage::Common::Client. The common client used to initalize the service.
135
+ #
136
+ # The valid set of options include:
137
+ # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
138
+ # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
139
+ # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
140
+ # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
141
+ # * Azure::Storage::Common::Client: The common client used to initalize the service. This client can be initalized and used repeatedly.
142
+ # * Anonymous Table: only +:storage_table_host+, if it is to only access tables within a container
143
+ #
144
+ # Additional notes:
145
+ # * Specified hosts can be set when use account name with access key or sas token
146
+ # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
147
+ # * Storage emulator always use path style URI
148
+ # * +:ca_file+ is independent.
149
+ #
150
+ # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
151
+ def initialize(options = {}, &block)
152
+ service_options = options.clone
153
+ client_config = service_options[:client] ||= Azure::Storage::Common::Client::create(service_options, &block)
154
+ @user_agent_prefix = service_options[:user_agent_prefix] if service_options[:user_agent_prefix]
155
+ @api_version = service_options[:api_version] || Azure::Storage::Table::Default::STG_VERSION
156
+ signer = service_options[:signer] || client_config.signer || Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key)
157
+ signer.api_ver = @api_version if signer.is_a? Azure::Storage::Common::Core::Auth::SharedAccessSignatureSigner
158
+ super(signer, client_config.storage_account_name, service_options, &block)
159
+ @storage_service_host[:primary] = client.storage_table_host
160
+ @storage_service_host[:secondary] = client.storage_table_host true
161
+ end
162
+
163
+ # Public: Creates new table in the storage account
164
+ #
165
+ # ==== Attributes
166
+ #
167
+ # * +table_name+ - String. The table name
168
+ # * +options+ - Hash. Optional parameters.
169
+ #
170
+ # ==== Options
171
+ #
172
+ # Accepted key/value pairs in options parameter are:
173
+ #
174
+ # * +:timeout+ - Integer. A timeout in seconds.
175
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
176
+ # in the analytics logs when storage analytics logging is enabled.
177
+ # * +:accept+ - String. Specifies the accepted content-type of the response payload. Possible values are:
178
+ # :no_meta
179
+ # :min_meta
180
+ # :full_meta
181
+ # * +:prefer+ - String. Specifies whether the response should include the inserted entity in the payload. Possible values are:
182
+ # Azure::Storage::Common::HeaderConstants::PREFER_CONTENT
183
+ # Azure::Storage::Common::HeaderConstants::PREFER_NO_CONTENT
184
+ #
185
+ # See http://msdn.microsoft.com/en-us/library/azure/dd135729
186
+ #
187
+ # @return [nil] on success
188
+ def create_table(table_name, options = {})
189
+ headers = {
190
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(options[:accept]),
191
+ }
192
+ headers[Azure::Storage::Common::HeaderConstants::PREFER] = options[:prefer] unless options[:prefer].nil?
193
+ body = Serialization.hash_to_json("TableName" => table_name)
194
+
195
+ call(:post, collection_uri(new_query(options)), body, headers, options)
196
+ nil
197
+ end
198
+
199
+ # Public: Deletes the specified table and any data it contains.
200
+ #
201
+ # ==== Attributes
202
+ #
203
+ # * +table_name+ - String. The table name
204
+ # * +options+ - Hash. Optional parameters.
205
+ #
206
+ # ==== Options
207
+ #
208
+ # Accepted key/value pairs in options parameter are:
209
+ # * +:timeout+ - Integer. A timeout in seconds.
210
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
211
+ # in the analytics logs when storage analytics logging is enabled.
212
+ #
213
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179387
214
+ #
215
+ # Returns nil on success
216
+ def delete_table(table_name, options = {})
217
+ call(:delete, table_uri(table_name, new_query(options)), nil, {}, options)
218
+ nil
219
+ end
220
+
221
+ # Public: Gets the table.
222
+ #
223
+ # ==== Attributes
224
+ #
225
+ # * +table_name+ - String. The table name
226
+ # * +options+ - Hash. Optional parameters.
227
+ #
228
+ # ==== Options
229
+ #
230
+ # Accepted key/value pairs in options parameter are:
231
+ # * +:timeout+ - Integer. A timeout in seconds.
232
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
233
+ # in the analytics logs when storage analytics logging is enabled.
234
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
235
+ # which location the request should be sent to.
236
+ #
237
+ # Returns the last updated time for the table
238
+ def get_table(table_name, options = {})
239
+ headers = {
240
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(:full_meta),
241
+ }
242
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
243
+ response = call(:get, table_uri(table_name, new_query(options), options), nil, headers, options)
244
+ Serialization.table_entries_from_json(response.body)
245
+ rescue => e
246
+ raise_with_response(e, response)
247
+ end
248
+
249
+ # Public: Gets a list of all tables on the account.
250
+ #
251
+ # ==== Attributes
252
+ #
253
+ # * +options+ - Hash. Optional parameters.
254
+ #
255
+ # ==== Options
256
+ #
257
+ # Accepted key/value pairs in options parameter are:
258
+ # * +:next_table_token+ - String. A token used to enumerate the next page of results, when the list of tables is
259
+ # larger than a single operation can return at once. (optional)
260
+ # * +:timeout+ - Integer. A timeout in seconds.
261
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
262
+ # in the analytics logs when storage analytics logging is enabled.
263
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
264
+ # which location the request should be sent to.
265
+ # * +:accept+ - String. Specifies the accepted content-type of the response payload. Possible values are:
266
+ # :no_meta
267
+ # :min_meta
268
+ # :full_meta
269
+ #
270
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179405
271
+ #
272
+ # Returns an array with an extra continuation_token property on success
273
+ def query_tables(options = {})
274
+ query = new_query(options)
275
+ query[TableConstants::NEXT_TABLE_NAME] = options[:next_table_token] if options[:next_table_token]
276
+
277
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
278
+ uri = collection_uri(query, options)
279
+
280
+ headers = {
281
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(options[:accept]),
282
+ }
283
+
284
+ response = call(:get, uri, nil, headers, options)
285
+ entries = Serialization.table_entries_from_json(response.body) || []
286
+ values = Azure::Storage::Common::Service::EnumerationResults.new(entries)
287
+ values.continuation_token = response.headers[TableConstants::CONTINUATION_NEXT_TABLE_NAME]
288
+ values
289
+ rescue => e
290
+ raise_with_response(e, response)
291
+ end
292
+
293
+ # Public: Gets the access control list (ACL) for the table.
294
+ #
295
+ # ==== Attributes
296
+ #
297
+ # * +table_name+ - String. The table name
298
+ # * +options+ - Hash. Optional parameters.
299
+ #
300
+ # ==== Options
301
+ #
302
+ # Accepted key/value pairs in options parameter are:
303
+ # * +:timeout+ - Integer. A timeout in seconds.
304
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
305
+ # in the analytics logs when storage analytics logging is enabled.
306
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
307
+ # which location the request should be sent to.
308
+ #
309
+ # See http://msdn.microsoft.com/en-us/library/azure/jj159100
310
+ #
311
+ # Returns a list of Azure::Storage::Entity::SignedIdentifier instances
312
+ def get_table_acl(table_name, options = {})
313
+ query = new_query(options)
314
+ query[Azure::Storage::Common::QueryStringConstants::COMP] = Azure::Storage::Common::QueryStringConstants::ACL
315
+
316
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
317
+ response = call(:get, generate_uri(table_name, query, options), nil, { "x-ms-version" => "2012-02-12" }, options)
318
+
319
+ signed_identifiers = []
320
+ signed_identifiers = Serialization.signed_identifiers_from_xml response.body unless response.body == nil || response.body.length < 1
321
+ signed_identifiers
322
+ rescue => e
323
+ raise_with_response(e, response)
324
+ end
325
+
326
+ # Public: Sets the access control list (ACL) for the table.
327
+ #
328
+ # ==== Attributes
329
+ #
330
+ # * +table_name+ - String. The table name
331
+ # * +options+ - Hash. Optional parameters.
332
+ #
333
+ # ==== Options
334
+ #
335
+ # Accepted key/value pairs in options parameter are:
336
+ # * +:signed_identifiers+ - Array. A list of Azure::Storage::Entity::SignedIdentifier instances
337
+ # * +:timeout+ - Integer. A timeout in seconds.
338
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
339
+ # in the analytics logs when storage analytics logging is enabled.
340
+ #
341
+ # See http://msdn.microsoft.com/en-us/library/azure/jj159102
342
+ #
343
+ # Returns nil on success
344
+ def set_table_acl(table_name, options = {})
345
+ query = new_query(options)
346
+ query[Azure::Storage::Common::QueryStringConstants::COMP] = Azure::Storage::Common::QueryStringConstants::ACL
347
+
348
+ uri = generate_uri(table_name, query)
349
+ body = nil
350
+ body = Serialization.signed_identifiers_to_xml options[:signed_identifiers] if options[:signed_identifiers] && options[:signed_identifiers].length > 0
351
+
352
+ call(:put, uri, body, { "x-ms-version" => "2012-02-12" }, options)
353
+ nil
354
+ end
355
+
356
+ # Public: Inserts new entity to the table.
357
+ #
358
+ #
359
+ # ==== Attributes
360
+ #
361
+ # * +table_name+ - String. The table name
362
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
363
+ # * +options+ - Hash. Optional parameters.
364
+ #
365
+ # ==== Options
366
+ #
367
+ # Accepted key/value pairs in options parameter are:
368
+ # * +:timeout+ - Integer. A timeout in seconds.
369
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
370
+ # in the analytics logs when storage analytics logging is enabled.
371
+ # * +:accept+ - String. Specifies the accepted content-type of the response payload. Possible values are:
372
+ # :no_meta
373
+ # :min_meta
374
+ # :full_meta
375
+ #
376
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179433
377
+ #
378
+ # Returns a Azure::Storage::Entity::Table::Entity
379
+ def insert_entity(table_name, entity_values, options = {})
380
+ body = Serialization.hash_to_json(entity_values)
381
+ #time = EdmType::to_edm_time(Time.now)
382
+ headers = {
383
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(options[:accept])
384
+ }
385
+ response = call(:post, entities_uri(table_name, nil, nil, new_query(options)), body, headers, options)
386
+ result = Serialization.entity_from_json(response.body)
387
+ result.etag = response.headers[Azure::Storage::Common::HeaderConstants::ETAG] if result.etag.nil?
388
+ result
389
+ rescue => e
390
+ raise_with_response(e, response)
391
+ end
392
+
393
+ # Public: Queries entities for the given table name
394
+ #
395
+ # ==== Attributes
396
+ #
397
+ # * +table_name+ - String. The table name
398
+ # * +options+ - Hash. Optional parameters.
399
+ #
400
+ # ==== Options
401
+ #
402
+ # Accepted key/value pairs in options parameter are:
403
+ # * +:partition_key+ - String. The partition key (optional)
404
+ # * +:row_key+ - String. The row key (optional)
405
+ # * +:select+ - Array. An array of property names to return (optional)
406
+ # * +:filter+ - String. A filter expression (optional)
407
+ # * +:top+ - Integer. A limit for the number of results returned (optional)
408
+ # * +:continuation_token+ - Hash. The continuation token.
409
+ # * +:timeout+ - Integer. A timeout in seconds.
410
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
411
+ # in the analytics logs when storage analytics logging is enabled.
412
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
413
+ # which location the request should be sent to.
414
+ # * +:accept+ - String. Specifies the accepted content-type of the response payload. Possible values are:
415
+ # :no_meta
416
+ # :min_meta
417
+ # :full_meta
418
+ #
419
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179421
420
+ #
421
+ # Returns an array with an extra continuation_token property on success
422
+ def query_entities(table_name, options = {})
423
+ query = new_query(options)
424
+ query[Azure::Storage::Common::QueryStringConstants::SELECT] = options[:select].join "," if options[:select]
425
+ query[Azure::Storage::Common::QueryStringConstants::FILTER] = options[:filter] if options[:filter]
426
+ query[Azure::Storage::Common::QueryStringConstants::TOP] = options[:top].to_s if options[:top] unless options[:partition_key] && options[:row_key]
427
+ query[Azure::Storage::Common::QueryStringConstants::NEXT_PARTITION_KEY] = options[:continuation_token][:next_partition_key] if options[:continuation_token] && options[:continuation_token][:next_partition_key]
428
+ query[Azure::Storage::Common::QueryStringConstants::NEXT_ROW_KEY] = options[:continuation_token][:next_row_key] if options[:continuation_token] && options[:continuation_token][:next_row_key]
429
+
430
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
431
+ uri = entities_uri(table_name, options[:partition_key], options[:row_key], query, options)
432
+
433
+ headers = {
434
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(options[:accept])
435
+ }
436
+
437
+ response = call(:get, uri, nil, headers, options)
438
+
439
+ entities = Azure::Storage::Common::Service::EnumerationResults.new.push(*Serialization.entities_from_json(response.body))
440
+
441
+ entities.continuation_token = nil
442
+ entities.continuation_token = {
443
+ next_partition_key: response.headers[TableConstants::CONTINUATION_NEXT_PARTITION_KEY],
444
+ next_row_key: response.headers[TableConstants::CONTINUATION_NEXT_ROW_KEY]
445
+ } if response.headers[TableConstants::CONTINUATION_NEXT_PARTITION_KEY]
446
+
447
+ entities
448
+ rescue => e
449
+ raise_with_response(e, response)
450
+ end
451
+
452
+ # Public: Updates an existing entity in a table. The Update Entity operation replaces
453
+ # the entire entity and can be used to remove properties.
454
+ #
455
+ # ==== Attributes
456
+ #
457
+ # * +table_name+ - String. The table name
458
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
459
+ # * +options+ - Hash. Optional parameters.
460
+ #
461
+ # ==== Options
462
+ #
463
+ # Accepted key/value pairs in options parameter are:
464
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
465
+ # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
466
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
467
+ # * +:timeout+ - Integer. A timeout in seconds.
468
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
469
+ # in the analytics logs when storage analytics logging is enabled.
470
+ #
471
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179427
472
+ #
473
+ # Returns the ETag for the entity on success
474
+ def update_entity(table_name, entity_values, options = {})
475
+ if_match = "*"
476
+ if_match = options[:if_match] if options[:if_match]
477
+
478
+ uri = entities_uri(table_name,
479
+ entity_values[:PartitionKey] || entity_values["PartitionKey"],
480
+ entity_values[:RowKey] || entity_values["RowKey"], new_query(options))
481
+
482
+ headers = {}
483
+ headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists]
484
+
485
+ body = Serialization.hash_to_json(entity_values)
486
+
487
+ response = call(:put, uri, body, headers, options)
488
+ response.headers["etag"]
489
+ rescue => e
490
+ raise_with_response(e, response)
491
+ end
492
+
493
+ # Public: Updates an existing entity by updating the entity's properties. This operation
494
+ # does not replace the existing entity, as the update_entity operation does.
495
+ #
496
+ # ==== Attributes
497
+ #
498
+ # * +table_name+ - String. The table name
499
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
500
+ # * +options+ - Hash. Optional parameters.
501
+ #
502
+ # ==== Options
503
+ #
504
+ # Accepted key/value pairs in options parameter are:
505
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
506
+ # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
507
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
508
+ # * +:timeout+ - Integer. A timeout in seconds.
509
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
510
+ # in the analytics logs when storage analytics logging is enabled.
511
+ #
512
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179392
513
+ #
514
+ # Returns the ETag for the entity on success
515
+ def merge_entity(table_name, entity_values, options = {})
516
+ if_match = "*"
517
+ if_match = options[:if_match] if options[:if_match]
518
+
519
+ uri = entities_uri(table_name,
520
+ entity_values[:PartitionKey] || entity_values["PartitionKey"],
521
+ entity_values[:RowKey] || entity_values["RowKey"], new_query(options))
522
+
523
+ headers = { "X-HTTP-Method" => "MERGE" }
524
+ headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists]
525
+
526
+ body = Serialization.hash_to_json(entity_values)
527
+
528
+ response = call(:post, uri, body, headers, options)
529
+ response.headers["etag"]
530
+ rescue => e
531
+ raise_with_response(e, response)
532
+ end
533
+
534
+ # Public: Inserts or updates an existing entity within a table by merging new property values into the entity.
535
+ #
536
+ # ==== Attributes
537
+ #
538
+ # * +table_name+ - String. The table name
539
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
540
+ # * +options+ - Hash. Optional parameters.
541
+ #
542
+ # ==== Options
543
+ #
544
+ # Accepted key/value pairs in options parameter are:
545
+ # * +:timeout+ - Integer. A timeout in seconds.
546
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
547
+ # in the analytics logs when storage analytics logging is enabled.
548
+ #
549
+ # See http://msdn.microsoft.com/en-us/library/azure/hh452241
550
+ #
551
+ # Returns the ETag for the entity on success
552
+ def insert_or_merge_entity(table_name, entity_values, options = {})
553
+ options[:create_if_not_exists] = true
554
+ merge_entity(table_name, entity_values, options)
555
+ end
556
+
557
+ # Public: Inserts or updates a new entity into a table.
558
+ #
559
+ # ==== Attributes
560
+ #
561
+ # * +table_name+ - String. The table name
562
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
563
+ # * +options+ - Hash. Optional parameters.
564
+ #
565
+ # ==== Options
566
+ #
567
+ # Accepted key/value pairs in options parameter are:
568
+ # * +:timeout+ - Integer. A timeout in seconds.
569
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
570
+ # in the analytics logs when storage analytics logging is enabled.
571
+ #
572
+ # See http://msdn.microsoft.com/en-us/library/azure/hh452242
573
+ #
574
+ # Returns the ETag for the entity on success
575
+ def insert_or_replace_entity(table_name, entity_values, options = {})
576
+ options[:create_if_not_exists] = true
577
+ update_entity(table_name, entity_values, options)
578
+ end
579
+
580
+ # Public: Deletes an existing entity in the table.
581
+ #
582
+ # ==== Attributes
583
+ #
584
+ # * +table_name+ - String. The table name
585
+ # * +partition_key+ - String. The partition key
586
+ # * +row_key+ - String. The row key
587
+ # * +options+ - Hash. Optional parameters.
588
+ #
589
+ # ==== Options
590
+ #
591
+ # Accepted key/value pairs in options parameter are:
592
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
593
+ # * +:timeout+ - Integer. A timeout in seconds.
594
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
595
+ # in the analytics logs when storage analytics logging is enabled.
596
+ #
597
+ # See http://msdn.microsoft.com/en-us/library/azure/dd135727
598
+ #
599
+ # Returns nil on success
600
+ def delete_entity(table_name, partition_key, row_key, options = {})
601
+ if_match = "*"
602
+ if_match = options[:if_match] if options[:if_match]
603
+
604
+ call(:delete, entities_uri(table_name, partition_key, row_key, new_query(options)), nil, { "If-Match" => if_match }, options)
605
+ nil
606
+ end
607
+
608
+ # Public: Executes a batch of operations.
609
+ #
610
+ # ==== Attributes
611
+ #
612
+ # * +batch+ - The Azure::Storage::Table::Batch instance to execute.
613
+ # * +options+ - Hash. Optional parameters.
614
+ #
615
+ # ==== Options
616
+ #
617
+ # Accepted key/value pairs in options parameter are:
618
+ # * +:timeout+ - Integer. A timeout in seconds.
619
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
620
+ # in the analytics logs when storage analytics logging is enabled.
621
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
622
+ # which location the request should be sent to.
623
+ #
624
+ # See http://msdn.microsoft.com/en-us/library/azure/dd894038
625
+ #
626
+ # Returns an array of results, one for each operation in the batch
627
+ def execute_batch(batch, options = {})
628
+ headers = {
629
+ Azure::Storage::Common::HeaderConstants::CONTENT_TYPE => "multipart/mixed; boundary=#{batch.batch_id}",
630
+ Azure::Storage::Common::HeaderConstants::ACCEPT => Serialization.get_accept_string(options[:accept]),
631
+ "Accept-Charset" => "UTF-8"
632
+ }
633
+
634
+ body = batch.to_body(self)
635
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
636
+ response = call(:post, generate_uri("/$batch", new_query(options), options), body, headers, options, true)
637
+ batch.parse_response(response)
638
+ rescue => e
639
+ raise_with_response(e, response)
640
+ end
641
+
642
+ # Public: Gets an existing entity in the table.
643
+ #
644
+ # ==== Attributes
645
+ #
646
+ # * +table_name+ - String. The table name
647
+ # * +partition_key+ - String. The partition key
648
+ # * +row_key+ - String. The row key
649
+ # * +options+ - Hash. Optional parameters.
650
+ #
651
+ # ==== Options
652
+ #
653
+ # Accepted key/value pairs in options parameter are:
654
+ # * +:timeout+ - Integer. A timeout in seconds.
655
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
656
+ # in the analytics logs when storage analytics logging is enabled.
657
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
658
+ # which location the request should be sent to.
659
+ #
660
+ # Returns an Azure::Storage::Table::Entity instance on success
661
+ def get_entity(table_name, partition_key, row_key, options = {})
662
+ options[:partition_key] = partition_key
663
+ options[:row_key] = row_key
664
+ results = query_entities(table_name, options)
665
+ results.length > 0 ? results[0] : nil
666
+ end
667
+
668
+ # Protected: Generate the URI for the collection of tables.
669
+ #
670
+ # Returns a URI
671
+ protected
672
+ def collection_uri(query = {}, options = {})
673
+ generate_uri("Tables", query, options)
674
+ end
675
+
676
+ # Public: Generate the URI for a specific table.
677
+ #
678
+ # ==== Attributes
679
+ #
680
+ # * +name+ - The table name. If this is a URI, we just return this
681
+ #
682
+ # Returns a URI
683
+ public
684
+ def table_uri(name, query = {}, options = {})
685
+ return name if name.kind_of? ::URI
686
+ generate_uri("Tables('#{name}')", query, options)
687
+ end
688
+
689
+ # Public: Generate the URI for an entity or group of entities in a table.
690
+ # If both the 'partition_key' and 'row_key' are specified, then the URI
691
+ # will match the entity under those specific keys.
692
+ #
693
+ # ==== Attributes
694
+ #
695
+ # * +table_name+ - The table name
696
+ # * +partition_key+ - The desired partition key (optional)
697
+ # * +row_key+ - The desired row key (optional)
698
+ #
699
+ # Returns a URI
700
+ public
701
+ def entities_uri(table_name, partition_key = nil, row_key = nil, query = {}, options = {})
702
+ return table_name if table_name.kind_of? ::URI
703
+
704
+ path = if partition_key && row_key
705
+ "%s(PartitionKey='%s',RowKey='%s')" % [
706
+ table_name.encode("UTF-8"), encodeODataUriValue(partition_key.encode("UTF-8")), encodeODataUriValue(row_key.encode("UTF-8"))
707
+ ]
708
+ else
709
+ "%s()" % table_name.encode("UTF-8")
710
+ end
711
+
712
+ uri = generate_uri(path, query, options)
713
+ qs = []
714
+ if query
715
+ query.each do | key, val |
716
+ key = key.encode("UTF-8")
717
+ val = val.encode("UTF-8")
718
+
719
+ if key[0] == "$"
720
+ qs.push "#{key}#{::URI.encode_www_form("" => val)}"
721
+ else
722
+ qs.push ::URI.encode_www_form(key => val)
723
+ end
724
+ end
725
+ end
726
+ uri.query = qs.join "&" if qs.length > 0
727
+ uri
728
+ end
729
+
730
+ protected
731
+ def encodeODataUriValues(values)
732
+ new_values = []
733
+ values.each do |value|
734
+ new_values.push encodeODataUriValue(value)
735
+ end
736
+ new_values
737
+ end
738
+
739
+ protected
740
+ def encodeODataUriValue(value)
741
+ # Replace each single quote (') with double single quotes ('') not double
742
+ # quotes (")
743
+ value = value.gsub("'", "''")
744
+
745
+ # Encode the special URL characters
746
+ value = URI.escape(value)
747
+
748
+ value
749
+ end
750
+
751
+ protected
752
+ def raise_with_response(e, response)
753
+ raise e if response.nil?
754
+ raise "Response header: #{response.headers.inspect}\nResponse body: #{response.body.inspect}\n#{e.inspect}\n#{e.backtrace.join("\n")}"
755
+ end
756
+
757
+ protected
758
+ def call(method, uri, body = nil, headers = {}, options = {}, is_batch = false)
759
+ headers["x-ms-version"] = @api_version ? @api_version : Default::STG_VERSION unless headers["x-ms-version"]
760
+ headers["User-Agent"] = @user_agent_prefix ? "#{@user_agent_prefix}; #{Default::USER_AGENT}" : Default::USER_AGENT
761
+ # Add JSON Content-Type header if is_batch is false because default is Atom.
762
+ headers[Azure::Storage::Common::HeaderConstants::CONTENT_TYPE] = Azure::Storage::Common::HeaderConstants::JSON_CONTENT_TYPE_VALUE unless is_batch
763
+ headers[Azure::Storage::Common::HeaderConstants::DATA_SERVICE_VERSION] = TableConstants::DEFAULT_DATA_SERVICE_VERSION
764
+ super(method, uri, body, headers, options)
765
+ end
766
+
767
+ protected
768
+ def new_query(options = {})
769
+ options[:timeout].nil? ? {} : { Azure::Storage::Common::QueryStringConstants::TIMEOUT => options[:timeout].to_s }
770
+ end
771
+ end
772
+ end
773
+ end
774
+
775
+ Azure::Storage::TableService = Azure::Storage::Table::TableService