azure-blob 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 363606d640d9841d39afc55797fde060f8a706bf0bbc85b9c6e2c1d0f51e40bb
4
- data.tar.gz: 4f6d315afcece7d4499891bf727311c7a79551fd3d8aaca3ed8d85d662c51ae0
3
+ metadata.gz: 0fd3246ce35e01647d7aaa6e5819320799d347a96d0b95b9bc4d4d89939ce9a1
4
+ data.tar.gz: b6ace232dddeed3415294e866115420a91935b7332c79494e5b17c9a2e91eb52
5
5
  SHA512:
6
- metadata.gz: 9ca4c5a7146c9a9d684c53a403e5d3270e1f6c0d51211bd467f297124e5771971840c78faa468ef620158ce98f7353097d6db66b830c59c34b4dab852985674f
7
- data.tar.gz: 1d452597c9d69a8d2b4dd97f06ab70d1c3d09a7dfe316ea98853dfd709b565bf0f3d6e605dc256fdfa5c73d93cd56643e8f034d054a27c7d8c1dab5914ff00f0
6
+ metadata.gz: f375dca9e699671f5fc165b8463b313f1cc823d5cb6f3f0666cffc22665fa7becb7d075f61dd0cbb131cf85ddc14c47b37b236d6d28b276b2d13b89cc7626660
7
+ data.tar.gz: 06bfe2d031d6deb2a58da359258df5923ffab3c23d6edf0a36ee4adb93ee4ac69a82d50d571e97b4cfdff10913f2e6c4a834f5338258c33f0b0a0336de1cde17
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.0] 2025-01-05
4
+
5
+ - Add optional `timeout` parameter to all API methods
6
+ - Add `AzureBlob::Http::TimeoutError` for handling Azure `OperationTimedOut` errors
7
+
3
8
  ## [0.6.0] 2025-12-01
4
9
 
5
10
  - Add support for AKS workload identity
@@ -70,6 +70,7 @@ module AzureBlob
70
70
  # Ending point in bytes
71
71
  def get_blob(key, options = {})
72
72
  uri = generate_uri("#{container}/#{key}")
73
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
73
74
 
74
75
  headers = {
75
76
  "x-ms-range": options[:start] && "bytes=#{options[:start]}-#{options[:end]}",
@@ -92,6 +93,7 @@ module AzureBlob
92
93
  def copy_blob(key, source_key, options = {})
93
94
  source_client = options.delete(:source_client) || self
94
95
  uri = generate_uri("#{container}/#{key}")
96
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
95
97
 
96
98
  source_uri = source_client.signed_uri(source_key, permissions: "r", expiry: Time.at(Time.now.to_i + 300).utc.iso8601)
97
99
 
@@ -114,6 +116,7 @@ module AzureBlob
114
116
  # Sets the value of the x-ms-delete-snapshots header. Default to +include+
115
117
  def delete_blob(key, options = {})
116
118
  uri = generate_uri("#{container}/#{key}")
119
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
117
120
 
118
121
  headers = {
119
122
  "x-ms-delete-snapshots": options[:delete_snapshots] || "include",
@@ -152,6 +155,7 @@ module AzureBlob
152
155
  prefix: options[:prefix].to_s.gsub(/\\/, "/"),
153
156
  }
154
157
  query[:maxresults] = options[:max_results] if options[:max_results]
158
+ query[:timeout] = options[:timeout] if options[:timeout]
155
159
  uri.query = URI.encode_www_form(**query)
156
160
 
157
161
  fetcher = ->(marker) do
@@ -172,6 +176,7 @@ module AzureBlob
172
176
  # To check for blob presence, look for `blob_exist?` as `get_blob_properties` raises on missing blob.
173
177
  def get_blob_properties(key, options = {})
174
178
  uri = generate_uri("#{container}/#{key}")
179
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
175
180
 
176
181
  response = Http.new(uri, additional_headers(options), signer:).head
177
182
 
@@ -196,7 +201,9 @@ module AzureBlob
196
201
  # Returns a hash of the blob's tags.
197
202
  def get_blob_tags(key, options = {})
198
203
  uri = generate_uri("#{container}/#{key}")
199
- uri.query = URI.encode_www_form(comp: "tags")
204
+ query = { comp: "tags" }
205
+ query[:timeout] = options[:timeout] if options[:timeout]
206
+ uri.query = URI.encode_www_form(**query)
200
207
  response = Http.new(uri, additional_headers(options), signer:).get
201
208
 
202
209
  Tags.from_response(response).to_h
@@ -209,7 +216,9 @@ module AzureBlob
209
216
  # This can be used to see if the container exist or obtain metadata.
210
217
  def get_container_properties(options = {})
211
218
  uri = generate_uri(container)
212
- uri.query = URI.encode_www_form(restype: "container")
219
+ query = { restype: "container" }
220
+ query[:timeout] = options[:timeout] if options[:timeout]
221
+ uri.query = URI.encode_www_form(**query)
213
222
  response = Http.new(uri, additional_headers(options), signer:, raise_on_error: false).head
214
223
 
215
224
  Container.new(response)
@@ -232,7 +241,9 @@ module AzureBlob
232
241
  headers[:"x-ms-blob-public-access"] = options[:public_access] if [ "container", "blob" ].include?(options[:public_access])
233
242
  headers.merge!(additional_headers(options))
234
243
 
235
- uri.query = URI.encode_www_form(restype: "container")
244
+ query = { restype: "container" }
245
+ query[:timeout] = options[:timeout] if options[:timeout]
246
+ uri.query = URI.encode_www_form(**query)
236
247
  response = Http.new(uri, headers, signer:).put
237
248
  end
238
249
 
@@ -241,7 +252,9 @@ module AzureBlob
241
252
  # Calls to {Delete Container}[https://learn.microsoft.com/en-us/rest/api/storageservices/delete-container]
242
253
  def delete_container(options = {})
243
254
  uri = generate_uri(container)
244
- uri.query = URI.encode_www_form(restype: "container")
255
+ query = { restype: "container" }
256
+ query[:timeout] = options[:timeout] if options[:timeout]
257
+ uri.query = URI.encode_www_form(**query)
245
258
  response = Http.new(uri, additional_headers(options), signer:).delete
246
259
  end
247
260
 
@@ -283,6 +296,7 @@ module AzureBlob
283
296
  # Will be saved on the blob in Azure.
284
297
  def create_append_blob(key, options = {})
285
298
  uri = generate_uri("#{container}/#{key}")
299
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
286
300
 
287
301
  headers = {
288
302
  "x-ms-blob-type": "AppendBlob",
@@ -306,7 +320,9 @@ module AzureBlob
306
320
  # The checksum must be the checksum of the block not the blob.
307
321
  def append_blob_block(key, content, options = {})
308
322
  uri = generate_uri("#{container}/#{key}")
309
- uri.query = URI.encode_www_form(comp: "appendblock")
323
+ query = { comp: "appendblock" }
324
+ query[:timeout] = options[:timeout] if options[:timeout]
325
+ uri.query = URI.encode_www_form(**query)
310
326
 
311
327
  headers = {
312
328
  "Content-Length": content_size(content),
@@ -330,7 +346,9 @@ module AzureBlob
330
346
  def put_blob_block(key, index, content, options = {})
331
347
  block_id = generate_block_id(index)
332
348
  uri = generate_uri("#{container}/#{key}")
333
- uri.query = URI.encode_www_form(comp: "block", blockid: block_id)
349
+ query = { comp: "block", blockid: block_id }
350
+ query[:timeout] = options[:timeout] if options[:timeout]
351
+ uri.query = URI.encode_www_form(**query)
334
352
 
335
353
  headers = {
336
354
  "Content-Length": content_size(content),
@@ -358,7 +376,9 @@ module AzureBlob
358
376
  block_list = BlockList.new(block_ids)
359
377
  content = block_list.to_s
360
378
  uri = generate_uri("#{container}/#{key}")
361
- uri.query = URI.encode_www_form(comp: "blocklist")
379
+ query = { comp: "blocklist" }
380
+ query[:timeout] = options[:timeout] if options[:timeout]
381
+ uri.query = URI.encode_www_form(**query)
362
382
 
363
383
  headers = {
364
384
  "Content-Length": content_size(content),
@@ -386,7 +406,7 @@ module AzureBlob
386
406
  block_size = options[:block_size] || DEFAULT_BLOCK_SIZE
387
407
  block_count = (content_size(content).to_f / block_size).ceil
388
408
  block_ids = block_count.times.map do |i|
389
- put_blob_block(key, i, content.read(block_size))
409
+ put_blob_block(key, i, content.read(block_size), options.slice(:timeout))
390
410
  end
391
411
 
392
412
  commit_blob_blocks(key, block_ids, options)
@@ -395,6 +415,7 @@ module AzureBlob
395
415
  def put_blob_single(key, content, options = {})
396
416
  content = StringIO.new(content) if content.is_a? String
397
417
  uri = generate_uri("#{container}/#{key}")
418
+ uri.query = URI.encode_www_form(timeout: options[:timeout]) if options[:timeout]
398
419
 
399
420
  headers = {
400
421
  "x-ms-blob-type": "BlockBlob",
@@ -21,6 +21,7 @@ module AzureBlob
21
21
  class FileNotFoundError < Error; end
22
22
  class ForbiddenError < Error; end
23
23
  class IntegrityError < Error; end
24
+ class TimeoutError < Error; end
24
25
 
25
26
  include REXML
26
27
 
@@ -99,6 +100,7 @@ module AzureBlob
99
100
 
100
101
  ERROR_CODE_MAPPINGS = {
101
102
  "Md5Mismatch" => IntegrityError,
103
+ "OperationTimedOut" => TimeoutError,
102
104
  }
103
105
 
104
106
  def sanitize_headers
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AzureBlob
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-blob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joé Dupuis