azure-storage 0.10.0.preview

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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/azure/storage.rb +58 -0
  3. data/lib/azure/storage/autoload.rb +71 -0
  4. data/lib/azure/storage/blob/append.rb +154 -0
  5. data/lib/azure/storage/blob/blob.rb +821 -0
  6. data/lib/azure/storage/blob/blob_service.rb +510 -0
  7. data/lib/azure/storage/blob/block.rb +264 -0
  8. data/lib/azure/storage/blob/container.rb +552 -0
  9. data/lib/azure/storage/blob/page.rb +380 -0
  10. data/lib/azure/storage/blob/serialization.rb +297 -0
  11. data/lib/azure/storage/client.rb +185 -0
  12. data/lib/azure/storage/configurable.rb +137 -0
  13. data/lib/azure/storage/core.rb +33 -0
  14. data/lib/azure/storage/core/auth/shared_access_signature.rb +27 -0
  15. data/lib/azure/storage/core/auth/shared_access_signature_generator.rb +194 -0
  16. data/lib/azure/storage/core/auth/shared_access_signature_signer.rb +49 -0
  17. data/lib/azure/storage/core/auth/shared_key.rb +125 -0
  18. data/lib/azure/storage/core/auth/shared_key_lite.rb +55 -0
  19. data/lib/azure/storage/core/auth/signer.rb +60 -0
  20. data/lib/azure/storage/core/autoload.rb +35 -0
  21. data/lib/azure/storage/core/client_options.rb +334 -0
  22. data/lib/azure/storage/core/client_options_error.rb +39 -0
  23. data/lib/azure/storage/core/constants.rb +1077 -0
  24. data/lib/azure/storage/core/error.rb +47 -0
  25. data/lib/azure/storage/core/filtered_service.rb +54 -0
  26. data/lib/azure/storage/core/http/debug_filter.rb +45 -0
  27. data/lib/azure/storage/core/http/http_error.rb +95 -0
  28. data/lib/azure/storage/core/http/http_filter.rb +62 -0
  29. data/lib/azure/storage/core/http/http_request.rb +182 -0
  30. data/lib/azure/storage/core/http/http_response.rb +105 -0
  31. data/lib/azure/storage/core/http/retry_policy.rb +83 -0
  32. data/lib/azure/storage/core/http/signer_filter.rb +42 -0
  33. data/lib/azure/storage/core/http_client.rb +63 -0
  34. data/lib/azure/storage/core/service.rb +55 -0
  35. data/lib/azure/storage/core/signed_service.rb +54 -0
  36. data/lib/azure/storage/core/sr.rb +83 -0
  37. data/lib/azure/storage/core/utility.rb +254 -0
  38. data/lib/azure/storage/queue/message.rb +39 -0
  39. data/lib/azure/storage/queue/queue.rb +37 -0
  40. data/lib/azure/storage/queue/queue_service.rb +580 -0
  41. data/lib/azure/storage/queue/serialization.rb +113 -0
  42. data/lib/azure/storage/service/access_policy.rb +35 -0
  43. data/lib/azure/storage/service/cors.rb +36 -0
  44. data/lib/azure/storage/service/cors_rule.rb +46 -0
  45. data/lib/azure/storage/service/enumeration_results.rb +30 -0
  46. data/lib/azure/storage/service/logging.rb +45 -0
  47. data/lib/azure/storage/service/metrics.rb +43 -0
  48. data/lib/azure/storage/service/retention_policy.rb +35 -0
  49. data/lib/azure/storage/service/serialization.rb +308 -0
  50. data/lib/azure/storage/service/signed_identifier.rb +39 -0
  51. data/lib/azure/storage/service/storage_service.rb +131 -0
  52. data/lib/azure/storage/service/storage_service_properties.rb +46 -0
  53. data/lib/azure/storage/table/auth/shared_key.rb +68 -0
  54. data/lib/azure/storage/table/auth/shared_key_lite.rb +53 -0
  55. data/lib/azure/storage/table/batch.rb +339 -0
  56. data/lib/azure/storage/table/batch_response.rb +127 -0
  57. data/lib/azure/storage/table/edmtype.rb +136 -0
  58. data/lib/azure/storage/table/entity.rb +40 -0
  59. data/lib/azure/storage/table/guid.rb +33 -0
  60. data/lib/azure/storage/table/query.rb +121 -0
  61. data/lib/azure/storage/table/serialization.rb +117 -0
  62. data/lib/azure/storage/table/table_service.rb +571 -0
  63. data/lib/azure/storage/version.rb +46 -0
  64. metadata +329 -0
@@ -0,0 +1,117 @@
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/service/serialization'
25
+
26
+ require 'azure/storage/table/guid'
27
+ require 'azure/storage/table/edmtype'
28
+
29
+ require 'time'
30
+ require 'date'
31
+
32
+ module Azure::Storage
33
+ module Table
34
+ module Serialization
35
+ include Azure::Storage::Service::Serialization
36
+
37
+ def self.hash_to_entry_xml(hash, id=nil, xml=Nokogiri::XML::Builder.new(:encoding => 'UTF-8'))
38
+ entry_namespaces = {
39
+ 'xmlns' => 'http://www.w3.org/2005/Atom',
40
+ 'xmlns:m' => 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata',
41
+ 'xmlns:d' => 'http://schemas.microsoft.com/ado/2007/08/dataservices'
42
+ }
43
+
44
+ xml.entry entry_namespaces do |entry|
45
+ id ? entry.id(id): entry.id
46
+ entry.updated Time.now.xmlschema
47
+ entry.title
48
+ entry.author do |author|
49
+ author.name
50
+ end
51
+ hash_to_content_xml(hash, entry)
52
+ end
53
+
54
+ xml
55
+ end
56
+
57
+ def self.hash_to_content_xml(hash, xml=Nokogiri::XML::Builder.new(:encoding => 'UTF-8'))
58
+ xml.send('content', :type => 'application/xml') do |content|
59
+ content.send('m:properties') do |properties|
60
+ hash.each do |key, val|
61
+ key = key.encode('UTF-8') if key.is_a? String and !key.encoding.names.include?('BINARY')
62
+ val = val.encode('UTF-8') if val.is_a? String and !val.encoding.names.include?('BINARY')
63
+
64
+ type = Azure::Storage::Table::EdmType.property_type(val)
65
+ attributes = {}
66
+ attributes['m:type'] = type unless type.nil? || type.empty?
67
+
68
+ if val.nil?
69
+ attributes['m:null'] = 'true'
70
+ properties.send("d:#{key}", attributes)
71
+ else
72
+ properties.send("d:#{key}", Azure::Storage::Table::EdmType.serialize_value(type, val), attributes)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ xml
79
+ end
80
+
81
+ def self.entries_from_feed_xml(xml)
82
+ xml = slopify(xml)
83
+ expect_node('feed', xml)
84
+
85
+ return nil unless (xml > 'entry').any?
86
+
87
+ results = []
88
+
89
+ if (xml > 'entry').count == 0
90
+ results.push hash_from_entry_xml((xml > 'entry'))
91
+ else
92
+ (xml > 'entry').each do |entry|
93
+ results.push hash_from_entry_xml(entry)
94
+ end
95
+ end
96
+
97
+ results
98
+ end
99
+
100
+ def self.hash_from_entry_xml(xml)
101
+ xml = slopify(xml)
102
+ expect_node('entry', xml)
103
+ result = {}
104
+ result[:etag] = xml['etag']
105
+ result[:updated] = Time.parse((xml > 'updated').text) if (xml > 'updated').any?
106
+ properties = {}
107
+ if (xml > 'content').any?
108
+ (xml > 'content').first.first_element_child.element_children.each do |prop|
109
+ properties[prop.name] = prop.text != '' ? Azure::Storage::Table::EdmType.unserialize_query_value(prop.text, prop['m:type']) : prop['null'] ? nil : ''
110
+ end
111
+ end
112
+ result[:properties] = properties
113
+ result
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,571 @@
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/service/storage_service'
25
+
26
+ require 'azure/storage/table/auth/shared_key'
27
+ require 'azure/storage/table/serialization'
28
+ require 'azure/storage/table/entity'
29
+
30
+ module Azure::Storage
31
+ module Table
32
+ class TableService < Azure::Storage::Service::StorageService
33
+
34
+ def initialize(options = {})
35
+ client_config = options[:client] || Azure::Storage
36
+ signer = options[:signer] || Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key)
37
+ super(signer, client_config.storage_account_name, options)
38
+ @host = client.storage_table_host
39
+ end
40
+
41
+ # Public: Creates new table in the storage account
42
+ #
43
+ # ==== Attributes
44
+ #
45
+ # * +table_name+ - String. The table name
46
+ # * +options+ - Hash. Optional parameters.
47
+ #
48
+ # ==== Options
49
+ #
50
+ # Accepted key/value pairs in options parameter are:
51
+ #
52
+ # * +:timeout+ - Integer. A timeout in seconds.
53
+ #
54
+ # See http://msdn.microsoft.com/en-us/library/azure/dd135729
55
+ #
56
+ # @return [nil] on success
57
+ def create_table(table_name, options={})
58
+ query = { }
59
+ query['timeout'] = options[:timeout].to_s if options[:timeout]
60
+
61
+ body = Azure::Storage::Table::Serialization.hash_to_entry_xml({"TableName" => table_name}).to_xml
62
+ call(:post, collection_uri(query), body)
63
+ nil
64
+ end
65
+
66
+ # Public: Deletes the specified table and any data it contains.
67
+ #
68
+ # ==== Attributes
69
+ #
70
+ # * +table_name+ - String. The table name
71
+ # * +options+ - Hash. Optional parameters.
72
+ #
73
+ # ==== Options
74
+ #
75
+ # Accepted key/value pairs in options parameter are:
76
+ # * +:timeout+ - Integer. A timeout in seconds.
77
+ #
78
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179387
79
+ #
80
+ # Returns nil on success
81
+ def delete_table(table_name, options={})
82
+ query = { }
83
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
84
+
85
+ call(:delete, table_uri(table_name, query))
86
+ nil
87
+ end
88
+
89
+ # Public: Gets the table.
90
+ #
91
+ # ==== Attributes
92
+ #
93
+ # * +table_name+ - String. The table name
94
+ # * +options+ - Hash. Optional parameters.
95
+ #
96
+ # ==== Options
97
+ #
98
+ # Accepted key/value pairs in options parameter are:
99
+ # * +:timeout+ - Integer. A timeout in seconds.
100
+ #
101
+ # Returns the last updated time for the table
102
+ def get_table(table_name, options={})
103
+ query = { }
104
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
105
+
106
+ response = call(:get, table_uri(table_name, query))
107
+ results = Azure::Storage::Table::Serialization.hash_from_entry_xml(response.body)
108
+ results[:updated]
109
+ end
110
+
111
+ # Public: Gets a list of all tables on the account.
112
+ #
113
+ # ==== Attributes
114
+ #
115
+ # * +options+ - Hash. Optional parameters.
116
+ #
117
+ # ==== Options
118
+ #
119
+ # Accepted key/value pairs in options parameter are:
120
+ # * +:next_table_token+ - String. A token used to enumerate the next page of results, when the list of tables is
121
+ # larger than a single operation can return at once. (optional)
122
+ # * +:timeout+ - Integer. A timeout in seconds.
123
+ #
124
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179405
125
+ #
126
+ # Returns an array with an extra continuation_token property on success
127
+ def query_tables(options={})
128
+ query = { }
129
+ query["NextTableName"] = options[:next_table_token] if options[:next_table_token]
130
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
131
+ uri = collection_uri(query)
132
+
133
+ response = call(:get, uri)
134
+ entries = Azure::Storage::Table::Serialization.entries_from_feed_xml(response.body) || []
135
+
136
+ values = Azure::Service::EnumerationResults.new(entries)
137
+ values.continuation_token = response.headers["x-ms-continuation-NextTableName"]
138
+ values
139
+ end
140
+
141
+ # Public: Gets the access control list (ACL) for the table.
142
+ #
143
+ # ==== Attributes
144
+ #
145
+ # * +table_name+ - String. The table name
146
+ # * +options+ - Hash. Optional parameters.
147
+ #
148
+ # ==== Options
149
+ #
150
+ # Accepted key/value pairs in options parameter are:
151
+ # * +:timeout+ - Integer. A timeout in seconds.
152
+ #
153
+ # See http://msdn.microsoft.com/en-us/library/azure/jj159100
154
+ #
155
+ # Returns a list of Azure::Storage::Entity::SignedIdentifier instances
156
+ def get_table_acl(table_name, options={})
157
+ query = { 'comp' => 'acl'}
158
+ query['timeout'] = options[:timeout].to_s if options[:timeout]
159
+
160
+ response = call(:get, generate_uri(table_name, query), nil, {'x-ms-version' => '2012-02-12'})
161
+
162
+ signed_identifiers = []
163
+ signed_identifiers = Azure::Storage::Table::Serialization.signed_identifiers_from_xml response.body unless response.body == nil or response.body.length < 1
164
+ signed_identifiers
165
+ end
166
+
167
+ # Public: Sets the access control list (ACL) for the table.
168
+ #
169
+ # ==== Attributes
170
+ #
171
+ # * +table_name+ - String. The table name
172
+ # * +options+ - Hash. Optional parameters.
173
+ #
174
+ # ==== Options
175
+ #
176
+ # Accepted key/value pairs in options parameter are:
177
+ # * +:signed_identifiers+ - Array. A list of Azure::Storage::Entity::SignedIdentifier instances
178
+ # * +:timeout+ - Integer. A timeout in seconds.
179
+ #
180
+ # See http://msdn.microsoft.com/en-us/library/azure/jj159102
181
+ #
182
+ # Returns nil on success
183
+ def set_table_acl(table_name, options={})
184
+ query = { 'comp' => 'acl'}
185
+ query['timeout'] = options[:timeout].to_s if options[:timeout]
186
+
187
+ uri = generate_uri(table_name, query)
188
+ body = nil
189
+ body = Azure::Storage::Table::Serialization.signed_identifiers_to_xml options[:signed_identifiers] if options[:signed_identifiers] && options[:signed_identifiers].length > 0
190
+
191
+ call(:put, uri, body, {'x-ms-version' => '2012-02-12'})
192
+ nil
193
+ end
194
+
195
+ # Public: Inserts new entity to the table.
196
+ #
197
+ #
198
+ # ==== Attributes
199
+ #
200
+ # * +table_name+ - String. The table name
201
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
202
+ # * +options+ - Hash. Optional parameters.
203
+ #
204
+ # ==== Options
205
+ #
206
+ # Accepted key/value pairs in options parameter are:
207
+ # * +:timeout+ - Integer. A timeout in seconds.
208
+ #
209
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179433
210
+ #
211
+ # Returns a Azure::Storage::Entity::Table::Entity
212
+ def insert_entity(table_name, entity_values, options={})
213
+ body = Azure::Storage::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
214
+
215
+ query = { }
216
+ query['timeout'] = options[:timeout].to_s if options[:timeout]
217
+
218
+ response = call(:post, entities_uri(table_name, nil, nil, query), body)
219
+
220
+ result = Azure::Storage::Table::Serialization.hash_from_entry_xml(response.body)
221
+
222
+ Entity.new do |entity|
223
+ entity.table = table_name
224
+ entity.updated = result[:updated]
225
+ entity.etag = response.headers['etag'] || result[:etag]
226
+ entity.properties = result[:properties]
227
+ end
228
+ end
229
+
230
+ # Public: Queries entities for the given table name
231
+ #
232
+ # ==== Attributes
233
+ #
234
+ # * +table_name+ - String. The table name
235
+ # * +options+ - Hash. Optional parameters.
236
+ #
237
+ # ==== Options
238
+ #
239
+ # Accepted key/value pairs in options parameter are:
240
+ # * +:partition_key+ - String. The partition key (optional)
241
+ # * +:row_key+ - String. The row key (optional)
242
+ # * +:select+ - Array. An array of property names to return (optional)
243
+ # * +:filter+ - String. A filter expression (optional)
244
+ # * +:top+ - Integer. A limit for the number of results returned (optional)
245
+ # * +:continuation_token+ - Hash. The continuation token.
246
+ # * +:timeout+ - Integer. A timeout in seconds.
247
+ #
248
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179421
249
+ #
250
+ # Returns an array with an extra continuation_token property on success
251
+ def query_entities(table_name, options={})
252
+ query ={}
253
+ query["$select"] = options[:select].join ',' if options[:select]
254
+ query["$filter"] = options[:filter] if options[:filter]
255
+ query["$top"] = options[:top].to_s if options[:top] unless options[:partition_key] and options[:row_key]
256
+ query["NextPartitionKey"] = options[:continuation_token][:next_partition_key] if options[:continuation_token] and options[:continuation_token][:next_partition_key]
257
+ query["NextRowKey"] = options[:continuation_token][:next_row_key] if options[:continuation_token] and options[:continuation_token][:next_row_key]
258
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
259
+
260
+ uri = entities_uri(table_name, options[:partition_key], options[:row_key], query)
261
+ response = call(:get, uri, nil, { "DataServiceVersion" => "2.0;NetFx"})
262
+
263
+ entities = Azure::Service::EnumerationResults.new
264
+
265
+ results = (options[:partition_key] and options[:row_key]) ? [Azure::Storage::Table::Serialization.hash_from_entry_xml(response.body)] : Azure::Storage::Table::Serialization.entries_from_feed_xml(response.body)
266
+
267
+ results.each do |result|
268
+ entity = Entity.new do |e|
269
+ e.table = table_name
270
+ e.updated = result[:updated]
271
+ e.etag = response.headers["etag"] || result[:etag]
272
+ e.properties = result[:properties]
273
+ end
274
+ entities.push entity
275
+ end if results
276
+
277
+ entities.continuation_token = nil
278
+ entities.continuation_token = {
279
+ :next_partition_key=> response.headers["x-ms-continuation-NextPartitionKey"],
280
+ :next_row_key => response.headers["x-ms-continuation-NextRowKey"]
281
+ } if response.headers["x-ms-continuation-NextPartitionKey"]
282
+
283
+ entities
284
+ end
285
+
286
+ # Public: Updates an existing entity in a table. The Update Entity operation replaces
287
+ # the entire entity and can be used to remove properties.
288
+ #
289
+ # ==== Attributes
290
+ #
291
+ # * +table_name+ - String. The table name
292
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
293
+ # * +options+ - Hash. Optional parameters.
294
+ #
295
+ # ==== Options
296
+ #
297
+ # Accepted key/value pairs in options parameter are:
298
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
299
+ # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
300
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
301
+ # * +:timeout+ - Integer. A timeout in seconds.
302
+ #
303
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179427
304
+ #
305
+ # Returns the ETag for the entity on success
306
+ def update_entity(table_name, entity_values, options={})
307
+ if_match = "*"
308
+ if_match = options[:if_match] if options[:if_match]
309
+
310
+ query = { }
311
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
312
+
313
+ uri = entities_uri(table_name, entity_values["PartitionKey"], entity_values["RowKey"], query)
314
+
315
+ headers = {}
316
+ headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists]
317
+
318
+ body = Azure::Storage::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
319
+
320
+ response = call(:put, uri, body, headers)
321
+ response.headers["etag"]
322
+ end
323
+
324
+ # Public: Updates an existing entity by updating the entity's properties. This operation
325
+ # does not replace the existing entity, as the update_entity operation does.
326
+ #
327
+ # ==== Attributes
328
+ #
329
+ # * +table_name+ - String. The table name
330
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
331
+ # * +options+ - Hash. Optional parameters.
332
+ #
333
+ # ==== Options
334
+ #
335
+ # Accepted key/value pairs in options parameter are:
336
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
337
+ # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity,
338
+ # that entity will be inserted. If false, the operation will fail. (optional, Default=false)
339
+ # * +:timeout+ - Integer. A timeout in seconds.
340
+ #
341
+ # See http://msdn.microsoft.com/en-us/library/azure/dd179392
342
+ #
343
+ # Returns the ETag for the entity on success
344
+ def merge_entity(table_name, entity_values, options={})
345
+ if_match = "*"
346
+ if_match = options[:if_match] if options[:if_match]
347
+
348
+ query = { }
349
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
350
+
351
+ uri = entities_uri(table_name, entity_values["PartitionKey"], entity_values["RowKey"], query)
352
+
353
+ headers = { "X-HTTP-Method"=> "MERGE" }
354
+ headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists]
355
+
356
+ body = Azure::Storage::Table::Serialization.hash_to_entry_xml(entity_values).to_xml
357
+
358
+ response = call(:post, uri, body, headers)
359
+ response.headers["etag"]
360
+ end
361
+
362
+ # Public: Inserts or updates an existing entity within a table by merging new property values into the entity.
363
+ #
364
+ # ==== Attributes
365
+ #
366
+ # * +table_name+ - String. The table name
367
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
368
+ # * +options+ - Hash. Optional parameters.
369
+ #
370
+ # ==== Options
371
+ #
372
+ # Accepted key/value pairs in options parameter are:
373
+ # * +:timeout+ - Integer. A timeout in seconds.
374
+ #
375
+ # See http://msdn.microsoft.com/en-us/library/azure/hh452241
376
+ #
377
+ # Returns the ETag for the entity on success
378
+ def insert_or_merge_entity(table_name, entity_values, options={})
379
+ options[:create_if_not_exists] = true
380
+ merge_entity(table_name, entity_values, options)
381
+ end
382
+
383
+ # Public: Inserts or updates a new entity into a table.
384
+ #
385
+ # ==== Attributes
386
+ #
387
+ # * +table_name+ - String. The table name
388
+ # * +entity_values+ - Hash. A hash of the name/value pairs for the entity.
389
+ # * +options+ - Hash. Optional parameters.
390
+ #
391
+ # ==== Options
392
+ #
393
+ # Accepted key/value pairs in options parameter are:
394
+ # * +:timeout+ - Integer. A timeout in seconds.
395
+ #
396
+ # See http://msdn.microsoft.com/en-us/library/azure/hh452242
397
+ #
398
+ # Returns the ETag for the entity on success
399
+ def insert_or_replace_entity(table_name, entity_values, options={})
400
+ options[:create_if_not_exists] = true
401
+ update_entity(table_name, entity_values, options)
402
+ end
403
+
404
+ # Public: Deletes an existing entity in the table.
405
+ #
406
+ # ==== Attributes
407
+ #
408
+ # * +table_name+ - String. The table name
409
+ # * +partition_key+ - String. The partition key
410
+ # * +row_key+ - String. The row key
411
+ # * +options+ - Hash. Optional parameters.
412
+ #
413
+ # ==== Options
414
+ #
415
+ # Accepted key/value pairs in options parameter are:
416
+ # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*")
417
+ # * +:timeout+ - Integer. A timeout in seconds.
418
+ #
419
+ # See http://msdn.microsoft.com/en-us/library/azure/dd135727
420
+ #
421
+ # Returns nil on success
422
+ def delete_entity(table_name, partition_key, row_key, options={})
423
+ if_match = "*"
424
+ if_match = options[:if_match] if options[:if_match]
425
+
426
+ query = { }
427
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
428
+
429
+ call(:delete, entities_uri(table_name, partition_key, row_key, query), nil, { "If-Match"=> if_match })
430
+ nil
431
+ end
432
+
433
+ # Public: Executes a batch of operations.
434
+ #
435
+ # ==== Attributes
436
+ #
437
+ # * +batch+ - The Azure::Storage::Table::Batch instance to execute.
438
+ # * +options+ - Hash. Optional parameters.
439
+ #
440
+ # ==== Options
441
+ #
442
+ # Accepted key/value pairs in options parameter are:
443
+ # * +:timeout+ - Integer. A timeout in seconds.
444
+ #
445
+ # See http://msdn.microsoft.com/en-us/library/azure/dd894038
446
+ #
447
+ # Returns an array of results, one for each operation in the batch
448
+ def execute_batch(batch, options={})
449
+ headers = {
450
+ 'Content-Type' => "multipart/mixed; boundary=#{batch.batch_id}",
451
+ 'Accept' => 'application/atom+xml,application/xml',
452
+ 'Accept-Charset'=> 'UTF-8'
453
+ }
454
+
455
+ query = { }
456
+ query["timeout"] = options[:timeout].to_s if options[:timeout]
457
+
458
+ body = batch.to_body
459
+ response = call(:post, generate_uri('/$batch', query), body, headers)
460
+ batch.parse_response(response)
461
+ end
462
+
463
+ # Public: Gets an existing entity in the table.
464
+ #
465
+ # ==== Attributes
466
+ #
467
+ # * +table_name+ - String. The table name
468
+ # * +partition_key+ - String. The partition key
469
+ # * +row_key+ - String. The row key
470
+ # * +options+ - Hash. Optional parameters.
471
+ #
472
+ # ==== Options
473
+ #
474
+ # Accepted key/value pairs in options parameter are:
475
+ # * +:timeout+ - Integer. A timeout in seconds.
476
+ #
477
+ # Returns an Azure::Storage::Table::Entity instance on success
478
+ def get_entity(table_name, partition_key, row_key, options={})
479
+ options[:partition_key] = partition_key
480
+ options[:row_key] = row_key
481
+ results = query_entities(table_name, options)
482
+ results.length > 0 ? results[0] : nil
483
+ end
484
+
485
+ # Protected: Generate the URI for the collection of tables.
486
+ #
487
+ # Returns a URI
488
+ protected
489
+ def collection_uri(query={})
490
+ generate_uri("Tables", query)
491
+ end
492
+
493
+ # Public: Generate the URI for a specific table.
494
+ #
495
+ # ==== Attributes
496
+ #
497
+ # * +name+ - The table name. If this is a URI, we just return this
498
+ #
499
+ # Returns a URI
500
+ public
501
+ def table_uri(name, query={})
502
+ return name if name.kind_of? ::URI
503
+ generate_uri("Tables('#{name}')", query)
504
+ end
505
+
506
+ # Public: Generate the URI for an entity or group of entities in a table.
507
+ # If both the 'partition_key' and 'row_key' are specified, then the URI
508
+ # will match the entity under those specific keys.
509
+ #
510
+ # ==== Attributes
511
+ #
512
+ # * +table_name+ - The table name
513
+ # * +partition_key+ - The desired partition key (optional)
514
+ # * +row_key+ - The desired row key (optional)
515
+ #
516
+ # Returns a URI
517
+ public
518
+ def entities_uri(table_name, partition_key=nil, row_key=nil, query={})
519
+ return table_name if table_name.kind_of? ::URI
520
+
521
+ path = if partition_key && row_key
522
+ "%s(PartitionKey='%s',RowKey='%s')" % [
523
+ table_name.encode("UTF-8"), encodeODataUriValue(partition_key.encode("UTF-8")), encodeODataUriValue(row_key.encode("UTF-8"))
524
+ ]
525
+ else
526
+ "%s()" % table_name.encode("UTF-8")
527
+ end
528
+
529
+ uri = generate_uri(path)
530
+ qs = []
531
+ if query
532
+ query.each do | key, val |
533
+ key = key.encode("UTF-8")
534
+ val = val.encode("UTF-8")
535
+
536
+ if key[0] == "$"
537
+ qs.push "#{key}#{::URI.encode_www_form(""=>val)}"
538
+ else
539
+ qs.push ::URI.encode_www_form(key=>val)
540
+ end
541
+ end
542
+ end
543
+ uri.query = qs.join '&' if qs.length > 0
544
+ uri
545
+ end
546
+
547
+ protected
548
+ def encodeODataUriValues(values)
549
+ new_values = []
550
+ values.each do |value|
551
+ new_values.push encodeODataUriValue(value)
552
+ end
553
+ new_values
554
+ end
555
+
556
+ protected
557
+ def encodeODataUriValue(value)
558
+ # Replace each single quote (') with double single quotes ('') not double
559
+ # quotes (")
560
+ value = value.gsub("'", "''")
561
+
562
+ # Encode the special URL characters
563
+ value = URI.escape(value)
564
+
565
+ value
566
+ end
567
+ end
568
+ end
569
+ end
570
+
571
+ Azure::Storage::TableService = Azure::Storage::Table::TableService