google-cloud-storage 1.18.1 → 1.44.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +17 -30
  3. data/CHANGELOG.md +312 -0
  4. data/CONTRIBUTING.md +4 -5
  5. data/LOGGING.md +1 -1
  6. data/OVERVIEW.md +37 -5
  7. data/TROUBLESHOOTING.md +2 -8
  8. data/lib/google/cloud/storage/bucket/acl.rb +40 -40
  9. data/lib/google/cloud/storage/bucket/cors.rb +4 -1
  10. data/lib/google/cloud/storage/bucket/lifecycle.rb +259 -44
  11. data/lib/google/cloud/storage/bucket/list.rb +3 -3
  12. data/lib/google/cloud/storage/bucket.rb +1096 -172
  13. data/lib/google/cloud/storage/convert.rb +4 -3
  14. data/lib/google/cloud/storage/credentials.rb +16 -14
  15. data/lib/google/cloud/storage/errors.rb +7 -2
  16. data/lib/google/cloud/storage/file/acl.rb +181 -20
  17. data/lib/google/cloud/storage/file/list.rb +10 -8
  18. data/lib/google/cloud/storage/file/signer_v2.rb +36 -18
  19. data/lib/google/cloud/storage/file/signer_v4.rb +249 -61
  20. data/lib/google/cloud/storage/file/verifier.rb +2 -2
  21. data/lib/google/cloud/storage/file.rb +450 -84
  22. data/lib/google/cloud/storage/hmac_key/list.rb +182 -0
  23. data/lib/google/cloud/storage/hmac_key.rb +316 -0
  24. data/lib/google/cloud/storage/policy/binding.rb +246 -0
  25. data/lib/google/cloud/storage/policy/bindings.rb +196 -0
  26. data/lib/google/cloud/storage/policy/condition.rb +138 -0
  27. data/lib/google/cloud/storage/policy.rb +277 -24
  28. data/lib/google/cloud/storage/post_object.rb +20 -2
  29. data/lib/google/cloud/storage/project.rb +249 -50
  30. data/lib/google/cloud/storage/service.rb +479 -288
  31. data/lib/google/cloud/storage/version.rb +1 -1
  32. data/lib/google/cloud/storage.rb +86 -16
  33. data/lib/google-cloud-storage.rb +54 -7
  34. metadata +74 -27
@@ -16,7 +16,7 @@
16
16
  require "google/cloud/storage/version"
17
17
  require "google/apis/storage_v1"
18
18
  require "digest"
19
- require "mime/types"
19
+ require "mini_mime"
20
20
  require "pathname"
21
21
 
22
22
  module Google
@@ -38,24 +38,37 @@ module Google
38
38
 
39
39
  ##
40
40
  # Creates a new Service instance.
41
- def initialize project, credentials, retries: nil, timeout: nil
41
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
42
+ def initialize project, credentials, retries: nil,
43
+ timeout: nil, open_timeout: nil, read_timeout: nil,
44
+ send_timeout: nil, host: nil, quota_project: nil,
45
+ max_elapsed_time: nil, base_interval: nil, max_interval: nil,
46
+ multiplier: nil
42
47
  @project = project
43
48
  @credentials = credentials
44
49
  @service = API::StorageService.new
45
50
  @service.client_options.application_name = "gcloud-ruby"
46
51
  @service.client_options.application_version = \
47
52
  Google::Cloud::Storage::VERSION
48
- @service.client_options.open_timeout_sec = timeout
49
- @service.client_options.read_timeout_sec = timeout
50
- @service.client_options.send_timeout_sec = timeout
53
+ @service.client_options.open_timeout_sec = (open_timeout || timeout)
54
+ @service.client_options.read_timeout_sec = (read_timeout || timeout)
55
+ @service.client_options.send_timeout_sec = (send_timeout || timeout)
51
56
  @service.client_options.transparent_gzip_decompression = false
52
57
  @service.request_options.retries = retries || 3
53
58
  @service.request_options.header ||= {}
54
59
  @service.request_options.header["x-goog-api-client"] = \
55
60
  "gl-ruby/#{RUBY_VERSION} gccl/#{Google::Cloud::Storage::VERSION}"
56
61
  @service.request_options.header["Accept-Encoding"] = "gzip"
62
+ @service.request_options.quota_project = quota_project if quota_project
63
+ @service.request_options.max_elapsed_time = max_elapsed_time if max_elapsed_time
64
+ @service.request_options.base_interval = base_interval if base_interval
65
+ @service.request_options.max_interval = max_interval if max_interval
66
+ @service.request_options.multiplier = multiplier if multiplier
67
+ @service.request_options.add_invocation_id_header = true
57
68
  @service.authorization = @credentials.client if @credentials
69
+ @service.root_url = host if host
58
70
  end
71
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
59
72
 
60
73
  def service
61
74
  return mocked_service if mocked_service
@@ -69,21 +82,28 @@ module Google
69
82
 
70
83
  ##
71
84
  # Retrieves a list of buckets for the given project.
72
- def list_buckets prefix: nil, token: nil, max: nil, user_project: nil
85
+ def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, options: {}
73
86
  execute do
74
87
  service.list_buckets \
75
88
  @project, prefix: prefix, page_token: token, max_results: max,
76
- user_project: user_project(user_project)
89
+ user_project: user_project(user_project), options: options
77
90
  end
78
91
  end
79
92
 
80
93
  ##
81
94
  # Retrieves bucket by name.
82
95
  # Returns Google::Apis::StorageV1::Bucket.
83
- def get_bucket bucket_name, user_project: nil
96
+ def get_bucket bucket_name,
97
+ if_metageneration_match: nil,
98
+ if_metageneration_not_match: nil,
99
+ user_project: nil,
100
+ options: {}
84
101
  execute do
85
102
  service.get_bucket bucket_name,
86
- user_project: user_project(user_project)
103
+ if_metageneration_match: if_metageneration_match,
104
+ if_metageneration_not_match: if_metageneration_not_match,
105
+ user_project: user_project(user_project),
106
+ options: options
87
107
  end
88
108
  end
89
109
 
@@ -91,148 +111,191 @@ module Google
91
111
  # Creates a new bucket.
92
112
  # Returns Google::Apis::StorageV1::Bucket.
93
113
  def insert_bucket bucket_gapi, acl: nil, default_acl: nil,
94
- user_project: nil
114
+ user_project: nil, options: {}
95
115
  execute do
96
116
  service.insert_bucket \
97
117
  @project, bucket_gapi,
98
118
  predefined_acl: acl,
99
119
  predefined_default_object_acl: default_acl,
100
- user_project: user_project(user_project)
120
+ user_project: user_project(user_project),
121
+ options: options
101
122
  end
102
123
  end
103
124
 
104
125
  ##
105
126
  # Updates a bucket, including its ACL metadata.
106
- def patch_bucket bucket_name, bucket_gapi = nil, predefined_acl: nil,
107
- predefined_default_acl: nil, user_project: nil
127
+ def patch_bucket bucket_name,
128
+ bucket_gapi = nil,
129
+ predefined_acl: nil,
130
+ predefined_default_acl: nil,
131
+ if_metageneration_match: nil,
132
+ if_metageneration_not_match: nil,
133
+ user_project: nil,
134
+ options: {}
108
135
  bucket_gapi ||= Google::Apis::StorageV1::Bucket.new
109
136
  bucket_gapi.acl = [] if predefined_acl
110
137
  bucket_gapi.default_object_acl = [] if predefined_default_acl
111
138
 
139
+ if options[:retries].nil?
140
+ is_idempotent = retry? if_metageneration_match: if_metageneration_match
141
+ options = is_idempotent ? {} : { retries: 0 }
142
+ end
143
+
112
144
  execute do
113
- service.patch_bucket \
114
- bucket_name, bucket_gapi,
115
- predefined_acl: predefined_acl,
116
- predefined_default_object_acl: predefined_default_acl,
117
- user_project: user_project(user_project)
145
+ service.patch_bucket bucket_name,
146
+ bucket_gapi,
147
+ predefined_acl: predefined_acl,
148
+ predefined_default_object_acl: predefined_default_acl,
149
+ if_metageneration_match: if_metageneration_match,
150
+ if_metageneration_not_match: if_metageneration_not_match,
151
+ user_project: user_project(user_project),
152
+ options: options
118
153
  end
119
154
  end
120
155
 
121
156
  ##
122
157
  # Permanently deletes an empty bucket.
123
- def delete_bucket bucket_name, user_project: nil
158
+ def delete_bucket bucket_name,
159
+ if_metageneration_match: nil,
160
+ if_metageneration_not_match: nil,
161
+ user_project: nil,
162
+ options: {}
124
163
  execute do
125
164
  service.delete_bucket bucket_name,
126
- user_project: user_project(user_project)
165
+ if_metageneration_match: if_metageneration_match,
166
+ if_metageneration_not_match: if_metageneration_not_match,
167
+ user_project: user_project(user_project),
168
+ options: options
127
169
  end
128
170
  end
129
171
 
130
172
  ##
131
173
  # Locks retention policy on a bucket.
132
174
  def lock_bucket_retention_policy bucket_name, metageneration,
133
- user_project: nil
175
+ user_project: nil,
176
+ options: {}
134
177
  execute do
135
178
  service.lock_bucket_retention_policy \
136
179
  bucket_name, metageneration,
137
- user_project: user_project(user_project)
180
+ user_project: user_project(user_project),
181
+ options: options
138
182
  end
139
183
  end
140
184
 
141
185
  ##
142
186
  # Retrieves a list of ACLs for the given bucket.
143
- def list_bucket_acls bucket_name, user_project: nil
187
+ def list_bucket_acls bucket_name, user_project: nil, options: {}
144
188
  execute do
145
189
  service.list_bucket_access_controls \
146
- bucket_name, user_project: user_project(user_project)
190
+ bucket_name, user_project: user_project(user_project),
191
+ options: options
147
192
  end
148
193
  end
149
194
 
150
195
  ##
151
196
  # Creates a new bucket ACL.
152
- def insert_bucket_acl bucket_name, entity, role, user_project: nil
153
- new_acl = Google::Apis::StorageV1::BucketAccessControl.new(
154
- { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
155
- )
197
+ def insert_bucket_acl bucket_name, entity, role, user_project: nil, options: {}
198
+ params = { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
199
+ new_acl = Google::Apis::StorageV1::BucketAccessControl.new(**params)
200
+ if options[:retries].nil?
201
+ options = options.merge({ retries: 0 })
202
+ end
156
203
  execute do
157
204
  service.insert_bucket_access_control \
158
- bucket_name, new_acl, user_project: user_project(user_project)
205
+ bucket_name, new_acl, user_project: user_project(user_project),
206
+ options: options
159
207
  end
160
208
  end
161
209
 
162
210
  ##
163
211
  # Permanently deletes a bucket ACL.
164
- def delete_bucket_acl bucket_name, entity, user_project: nil
212
+ def delete_bucket_acl bucket_name, entity, user_project: nil, options: {}
213
+ if options[:retries].nil?
214
+ options = options.merge({ retries: 0 })
215
+ end
165
216
  execute do
166
217
  service.delete_bucket_access_control \
167
- bucket_name, entity, user_project: user_project(user_project)
218
+ bucket_name, entity, user_project: user_project(user_project),
219
+ options: options
168
220
  end
169
221
  end
170
222
 
171
223
  ##
172
224
  # Retrieves a list of default ACLs for the given bucket.
173
- def list_default_acls bucket_name, user_project: nil
225
+ def list_default_acls bucket_name, user_project: nil, options: {}
174
226
  execute do
175
227
  service.list_default_object_access_controls \
176
- bucket_name, user_project: user_project(user_project)
228
+ bucket_name, user_project: user_project(user_project),
229
+ options: options
177
230
  end
178
231
  end
179
232
 
180
233
  ##
181
234
  # Creates a new default ACL.
182
- def insert_default_acl bucket_name, entity, role, user_project: nil
183
- new_acl = Google::Apis::StorageV1::ObjectAccessControl.new(
184
- { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
185
- )
235
+ def insert_default_acl bucket_name, entity, role, user_project: nil, options: {}
236
+ if options[:retries].nil?
237
+ options = options.merge({ retries: 0 })
238
+ end
239
+ param = { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
240
+ new_acl = Google::Apis::StorageV1::ObjectAccessControl.new(**param)
186
241
  execute do
187
242
  service.insert_default_object_access_control \
188
- bucket_name, new_acl, user_project: user_project(user_project)
243
+ bucket_name, new_acl, user_project: user_project(user_project),
244
+ options: options
189
245
  end
190
246
  end
191
247
 
192
248
  ##
193
249
  # Permanently deletes a default ACL.
194
- def delete_default_acl bucket_name, entity, user_project: nil
250
+ def delete_default_acl bucket_name, entity, user_project: nil, options: {}
251
+ if options[:retries].nil?
252
+ options = options.merge({ retries: 0 })
253
+ end
195
254
  execute do
196
255
  service.delete_default_object_access_control \
197
- bucket_name, entity, user_project: user_project(user_project)
256
+ bucket_name, entity, user_project: user_project(user_project),
257
+ options: options
198
258
  end
199
259
  end
200
260
 
201
261
  ##
202
262
  # Returns Google::Apis::StorageV1::Policy
203
- def get_bucket_policy bucket_name, user_project: nil
263
+ def get_bucket_policy bucket_name, requested_policy_version: nil, user_project: nil,
264
+ options: {}
204
265
  # get_bucket_iam_policy(bucket, fields: nil, quota_user: nil,
205
266
  # user_ip: nil, options: nil)
206
267
  execute do
207
- service.get_bucket_iam_policy \
208
- bucket_name, user_project: user_project(user_project)
268
+ service.get_bucket_iam_policy bucket_name, options_requested_policy_version: requested_policy_version,
269
+ user_project: user_project(user_project), options: options
209
270
  end
210
271
  end
211
272
 
212
273
  ##
213
274
  # Returns Google::Apis::StorageV1::Policy
214
- def set_bucket_policy bucket_name, new_policy, user_project: nil
275
+ def set_bucket_policy bucket_name, new_policy, user_project: nil, options: {}
215
276
  execute do
216
277
  service.set_bucket_iam_policy \
217
- bucket_name, new_policy, user_project: user_project(user_project)
278
+ bucket_name, new_policy, user_project: user_project(user_project), options: options
218
279
  end
219
280
  end
220
281
 
221
282
  ##
222
283
  # Returns Google::Apis::StorageV1::TestIamPermissionsResponse
223
- def test_bucket_permissions bucket_name, permissions, user_project: nil
284
+ def test_bucket_permissions bucket_name, permissions, user_project: nil, options: {}
224
285
  execute do
225
286
  service.test_bucket_iam_permissions \
226
- bucket_name, permissions, user_project: user_project(user_project)
287
+ bucket_name, permissions, user_project: user_project(user_project),
288
+ options: options
227
289
  end
228
290
  end
229
291
 
230
292
  ##
231
293
  # Retrieves a list of Pub/Sub notification subscriptions for a bucket.
232
- def list_notifications bucket_name, user_project: nil
294
+ def list_notifications bucket_name, user_project: nil, options: {}
233
295
  execute do
234
296
  service.list_notifications bucket_name,
235
- user_project: user_project(user_project)
297
+ user_project: user_project(user_project),
298
+ options: options
236
299
  end
237
300
  end
238
301
 
@@ -240,133 +303,244 @@ module Google
240
303
  # Creates a new Pub/Sub notification subscription for a bucket.
241
304
  def insert_notification bucket_name, topic_name, custom_attrs: nil,
242
305
  event_types: nil, prefix: nil, payload: nil,
243
- user_project: nil
244
- new_notification = Google::Apis::StorageV1::Notification.new(
306
+ user_project: nil, options: {}
307
+ params =
245
308
  { custom_attributes: custom_attrs,
246
309
  event_types: event_types(event_types),
247
310
  object_name_prefix: prefix,
248
311
  payload_format: payload_format(payload),
249
312
  topic: topic_path(topic_name) }.delete_if { |_k, v| v.nil? }
250
- )
313
+ new_notification = Google::Apis::StorageV1::Notification.new(**params)
314
+
315
+ if options[:retries].nil?
316
+ options = options.merge({ retries: 0 })
317
+ end
251
318
 
252
319
  execute do
253
320
  service.insert_notification \
254
321
  bucket_name, new_notification,
255
- user_project: user_project(user_project)
322
+ user_project: user_project(user_project),
323
+ options: options
256
324
  end
257
325
  end
258
326
 
259
327
  ##
260
328
  # Retrieves a Pub/Sub notification subscription for a bucket.
261
- def get_notification bucket_name, notification_id, user_project: nil
329
+ def get_notification bucket_name, notification_id, user_project: nil, options: {}
262
330
  execute do
263
331
  service.get_notification bucket_name, notification_id,
264
- user_project: user_project(user_project)
332
+ user_project: user_project(user_project),
333
+ options: options
265
334
  end
266
335
  end
267
336
 
268
337
  ##
269
338
  # Deletes a new Pub/Sub notification subscription for a bucket.
270
- def delete_notification bucket_name, notification_id, user_project: nil
339
+ def delete_notification bucket_name, notification_id, user_project: nil, options: {}
271
340
  execute do
272
341
  service.delete_notification bucket_name, notification_id,
273
- user_project: user_project(user_project)
342
+ user_project: user_project(user_project),
343
+ options: options
274
344
  end
275
345
  end
276
346
 
277
347
  ##
278
348
  # Retrieves a list of files matching the criteria.
279
349
  def list_files bucket_name, delimiter: nil, max: nil, token: nil,
280
- prefix: nil, versions: nil, user_project: nil
350
+ prefix: nil, versions: nil, user_project: nil,
351
+ options: {}
281
352
  execute do
282
353
  service.list_objects \
283
354
  bucket_name, delimiter: delimiter, max_results: max,
284
355
  page_token: token, prefix: prefix,
285
356
  versions: versions,
286
- user_project: user_project(user_project)
357
+ user_project: user_project(user_project),
358
+ options: options
287
359
  end
288
360
  end
289
361
 
290
362
  ##
291
363
  # Inserts a new file for the given bucket
292
- def insert_file bucket_name, source, path = nil, acl: nil,
293
- cache_control: nil, content_disposition: nil,
294
- content_encoding: nil, content_language: nil,
295
- content_type: nil, crc32c: nil, md5: nil, metadata: nil,
296
- storage_class: nil, key: nil, kms_key: nil,
297
- temporary_hold: nil, event_based_hold: nil,
298
- user_project: nil
299
- file_obj = Google::Apis::StorageV1::Object.new(
300
- { cache_control: cache_control, content_type: content_type,
301
- content_disposition: content_disposition, md5_hash: md5,
302
- content_encoding: content_encoding, crc32c: crc32c,
303
- content_language: content_language, metadata: metadata,
304
- storage_class: storage_class, temporary_hold: temporary_hold,
305
- event_based_hold: event_based_hold }.delete_if { |_k, v| v.nil? }
306
- )
364
+ def insert_file bucket_name,
365
+ source,
366
+ path = nil,
367
+ acl: nil,
368
+ cache_control: nil,
369
+ content_disposition: nil,
370
+ content_encoding: nil,
371
+ content_language: nil,
372
+ content_type: nil,
373
+ custom_time: nil,
374
+ crc32c: nil,
375
+ md5: nil,
376
+ metadata: nil,
377
+ storage_class: nil,
378
+ key: nil,
379
+ kms_key: nil,
380
+ temporary_hold: nil,
381
+ event_based_hold: nil,
382
+ if_generation_match: nil,
383
+ if_generation_not_match: nil,
384
+ if_metageneration_match: nil,
385
+ if_metageneration_not_match: nil,
386
+ user_project: nil,
387
+ options: {}
388
+ params = {
389
+ cache_control: cache_control,
390
+ content_type: content_type,
391
+ custom_time: custom_time,
392
+ content_disposition: content_disposition,
393
+ md5_hash: md5,
394
+ content_encoding: content_encoding,
395
+ crc32c: crc32c,
396
+ content_language: content_language,
397
+ metadata: metadata,
398
+ storage_class: storage_class,
399
+ temporary_hold: temporary_hold,
400
+ event_based_hold: event_based_hold
401
+ }.delete_if { |_k, v| v.nil? }
402
+ file_obj = Google::Apis::StorageV1::Object.new(**params)
307
403
  content_type ||= mime_type_for(path || Pathname(source).to_path)
308
404
 
405
+ if options[:retries].nil?
406
+ is_idempotent = retry? if_generation_match: if_generation_match
407
+ options = is_idempotent ? key_options(key) : key_options(key).merge(retries: 0)
408
+ else
409
+ options = key_options(key).merge options
410
+ end
411
+
309
412
  execute do
310
- service.insert_object \
311
- bucket_name, file_obj,
312
- name: path, predefined_acl: acl, upload_source: source,
313
- content_encoding: content_encoding, content_type: content_type,
314
- kms_key_name: kms_key, user_project: user_project(user_project),
315
- options: key_options(key)
413
+ service.insert_object bucket_name,
414
+ file_obj,
415
+ name: path,
416
+ predefined_acl: acl,
417
+ upload_source: source,
418
+ content_encoding: content_encoding,
419
+ content_type: content_type,
420
+ if_generation_match: if_generation_match,
421
+ if_generation_not_match: if_generation_not_match,
422
+ if_metageneration_match: if_metageneration_match,
423
+ if_metageneration_not_match: if_metageneration_not_match,
424
+ kms_key_name: kms_key,
425
+ user_project: user_project(user_project),
426
+ options: options
316
427
  end
317
428
  end
318
429
 
319
430
  ##
320
431
  # Retrieves an object or its metadata.
321
- def get_file bucket_name, file_path, generation: nil, key: nil,
322
- user_project: nil
432
+ def get_file bucket_name,
433
+ file_path,
434
+ generation: nil,
435
+ if_generation_match: nil,
436
+ if_generation_not_match: nil,
437
+ if_metageneration_match: nil,
438
+ if_metageneration_not_match: nil,
439
+ key: nil,
440
+ user_project: nil,
441
+ options: {}
323
442
  execute do
324
443
  service.get_object \
325
444
  bucket_name, file_path,
326
445
  generation: generation,
446
+ if_generation_match: if_generation_match,
447
+ if_generation_not_match: if_generation_not_match,
448
+ if_metageneration_match: if_metageneration_match,
449
+ if_metageneration_not_match: if_metageneration_not_match,
327
450
  user_project: user_project(user_project),
328
- options: key_options(key)
451
+ options: key_options(key).merge(options)
329
452
  end
330
453
  end
331
454
 
332
455
  ## Rewrite a file from source bucket/object to a
333
456
  # destination bucket/object.
334
- def rewrite_file source_bucket_name, source_file_path,
335
- destination_bucket_name, destination_file_path,
336
- file_gapi = nil, source_key: nil, destination_key: nil,
337
- destination_kms_key: nil, acl: nil, generation: nil,
338
- token: nil, user_project: nil
457
+ def rewrite_file source_bucket_name,
458
+ source_file_path,
459
+ destination_bucket_name,
460
+ destination_file_path,
461
+ file_gapi = nil,
462
+ source_key: nil,
463
+ destination_key: nil,
464
+ destination_kms_key: nil,
465
+ acl: nil,
466
+ generation: nil,
467
+ if_generation_match: nil,
468
+ if_generation_not_match: nil,
469
+ if_metageneration_match: nil,
470
+ if_metageneration_not_match: nil,
471
+ if_source_generation_match: nil,
472
+ if_source_generation_not_match: nil,
473
+ if_source_metageneration_match: nil,
474
+ if_source_metageneration_not_match: nil,
475
+ token: nil,
476
+ user_project: nil,
477
+ options: {}
339
478
  key_options = rewrite_key_options source_key, destination_key
479
+
480
+ if options[:retries].nil?
481
+ is_idempotent = retry? if_generation_match: if_generation_match
482
+ options = is_idempotent ? key_options : key_options.merge(retries: 0)
483
+ else
484
+ options = key_options.merge options
485
+ end
486
+
340
487
  execute do
341
- service.rewrite_object \
342
- source_bucket_name, source_file_path,
343
- destination_bucket_name, destination_file_path,
344
- file_gapi,
345
- destination_kms_key_name: destination_kms_key,
346
- destination_predefined_acl: acl,
347
- source_generation: generation,
348
- rewrite_token: token,
349
- user_project: user_project(user_project),
350
- options: key_options
488
+ service.rewrite_object source_bucket_name,
489
+ source_file_path,
490
+ destination_bucket_name,
491
+ destination_file_path,
492
+ file_gapi,
493
+ destination_kms_key_name: destination_kms_key,
494
+ destination_predefined_acl: acl,
495
+ source_generation: generation,
496
+ if_generation_match: if_generation_match,
497
+ if_generation_not_match: if_generation_not_match,
498
+ if_metageneration_match: if_metageneration_match,
499
+ if_metageneration_not_match: if_metageneration_not_match,
500
+ if_source_generation_match: if_source_generation_match,
501
+ if_source_generation_not_match: if_source_generation_not_match,
502
+ if_source_metageneration_match: if_source_metageneration_match,
503
+ if_source_metageneration_not_match: if_source_metageneration_not_match,
504
+ rewrite_token: token,
505
+ user_project: user_project(user_project),
506
+ options: options
351
507
  end
352
508
  end
353
509
 
354
510
  ## Copy a file from source bucket/object to a
355
511
  # destination bucket/object.
356
- def compose_file bucket_name, source_files, destination_path,
357
- destination_gapi, acl: nil, key: nil, user_project: nil
358
-
359
- compose_req = Google::Apis::StorageV1::ComposeRequest.new \
360
- source_objects: compose_file_source_objects(source_files),
361
- destination: destination_gapi
512
+ def compose_file bucket_name,
513
+ source_files,
514
+ destination_path,
515
+ destination_gapi,
516
+ acl: nil,
517
+ key: nil,
518
+ if_source_generation_match: nil,
519
+ if_generation_match: nil,
520
+ if_metageneration_match: nil,
521
+ user_project: nil,
522
+ options: {}
523
+
524
+ source_objects = compose_file_source_objects source_files, if_source_generation_match
525
+ compose_req = Google::Apis::StorageV1::ComposeRequest.new source_objects: source_objects,
526
+ destination: destination_gapi
527
+
528
+ if options[:retries].nil?
529
+ is_idempotent = retry? if_generation_match: if_generation_match
530
+ options = is_idempotent ? key_options(key) : key_options(key).merge(retries: 0)
531
+ else
532
+ options = key_options.merge options
533
+ end
362
534
 
363
535
  execute do
364
- service.compose_object \
365
- bucket_name, destination_path,
366
- compose_req,
367
- destination_predefined_acl: acl,
368
- user_project: user_project(user_project),
369
- options: key_options(key)
536
+ service.compose_object bucket_name,
537
+ destination_path,
538
+ compose_req,
539
+ destination_predefined_acl: acl,
540
+ if_generation_match: if_generation_match,
541
+ if_metageneration_match: if_metageneration_match,
542
+ user_project: user_project(user_project),
543
+ options: options
370
544
  end
371
545
  end
372
546
 
@@ -380,12 +554,12 @@ module Google
380
554
  # Apis::StorageV1::StorageService and Apis::Core::DownloadCommand at
381
555
  # the end of this file.
382
556
  def download_file bucket_name, file_path, target_path, generation: nil,
383
- key: nil, range: nil, user_project: nil
384
- options = key_options key
557
+ key: nil, range: nil, user_project: nil, options: {}
558
+ options = key_options(key).merge(options)
385
559
  options = range_header options, range
386
560
 
387
561
  execute do
388
- service.get_object_with_response \
562
+ service.get_object \
389
563
  bucket_name, file_path,
390
564
  download_dest: target_path, generation: generation,
391
565
  user_project: user_project(user_project),
@@ -395,59 +569,214 @@ module Google
395
569
 
396
570
  ##
397
571
  # Updates a file's metadata.
398
- def patch_file bucket_name, file_path, file_gapi = nil,
399
- predefined_acl: nil, user_project: nil
572
+ def patch_file bucket_name,
573
+ file_path,
574
+ file_gapi = nil,
575
+ generation: nil,
576
+ if_generation_match: nil,
577
+ if_generation_not_match: nil,
578
+ if_metageneration_match: nil,
579
+ if_metageneration_not_match: nil,
580
+ predefined_acl: nil,
581
+ user_project: nil,
582
+ options: {}
400
583
  file_gapi ||= Google::Apis::StorageV1::Object.new
584
+
585
+ if options[:retries].nil?
586
+ is_idempotent = retry? if_metageneration_match: if_metageneration_match
587
+ options = is_idempotent ? {} : { retries: 0 }
588
+ end
589
+
401
590
  execute do
402
- service.patch_object \
403
- bucket_name, file_path, file_gapi,
404
- predefined_acl: predefined_acl,
405
- user_project: user_project(user_project)
591
+ service.patch_object bucket_name,
592
+ file_path,
593
+ file_gapi,
594
+ generation: generation,
595
+ if_generation_match: if_generation_match,
596
+ if_generation_not_match: if_generation_not_match,
597
+ if_metageneration_match: if_metageneration_match,
598
+ if_metageneration_not_match: if_metageneration_not_match,
599
+ predefined_acl: predefined_acl,
600
+ user_project: user_project(user_project),
601
+ options: options
406
602
  end
407
603
  end
408
604
 
409
605
  ##
410
606
  # Permanently deletes a file.
411
- def delete_file bucket_name, file_path, generation: nil,
412
- user_project: nil
607
+ def delete_file bucket_name,
608
+ file_path,
609
+ generation: nil,
610
+ if_generation_match: nil,
611
+ if_generation_not_match: nil,
612
+ if_metageneration_match: nil,
613
+ if_metageneration_not_match: nil,
614
+ user_project: nil,
615
+ options: {}
616
+
617
+ if options[:retries].nil?
618
+ is_idempotent = retry? generation: generation, if_generation_match: if_generation_match
619
+ options = is_idempotent ? {} : { retries: 0 }
620
+ end
621
+
413
622
  execute do
414
623
  service.delete_object bucket_name, file_path,
415
624
  generation: generation,
416
- user_project: user_project(user_project)
625
+ if_generation_match: if_generation_match,
626
+ if_generation_not_match: if_generation_not_match,
627
+ if_metageneration_match: if_metageneration_match,
628
+ if_metageneration_not_match: if_metageneration_not_match,
629
+ user_project: user_project(user_project),
630
+ options: options
417
631
  end
418
632
  end
419
633
 
420
634
  ##
421
635
  # Retrieves a list of ACLs for the given file.
422
- def list_file_acls bucket_name, file_name, user_project: nil
636
+ def list_file_acls bucket_name, file_name, user_project: nil, options: {}
423
637
  execute do
424
638
  service.list_object_access_controls \
425
- bucket_name, file_name, user_project: user_project(user_project)
639
+ bucket_name, file_name, user_project: user_project(user_project),
640
+ options: options
426
641
  end
427
642
  end
428
643
 
429
644
  ##
430
645
  # Creates a new file ACL.
431
646
  def insert_file_acl bucket_name, file_name, entity, role,
432
- generation: nil, user_project: nil
433
- new_acl = Google::Apis::StorageV1::ObjectAccessControl.new(
434
- { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
435
- )
647
+ generation: nil, user_project: nil,
648
+ options: {}
649
+ if options[:retries].nil?
650
+ options = options.merge({ retries: 0 })
651
+ end
652
+ params = { entity: entity, role: role }.delete_if { |_k, v| v.nil? }
653
+ new_acl = Google::Apis::StorageV1::ObjectAccessControl.new(**params)
436
654
  execute do
437
655
  service.insert_object_access_control \
438
656
  bucket_name, file_name, new_acl,
439
- generation: generation, user_project: user_project(user_project)
657
+ generation: generation, user_project: user_project(user_project),
658
+ options: options
440
659
  end
441
660
  end
442
661
 
443
662
  ##
444
663
  # Permanently deletes a file ACL.
445
664
  def delete_file_acl bucket_name, file_name, entity, generation: nil,
446
- user_project: nil
665
+ user_project: nil, options: {}
666
+ if options[:retries].nil?
667
+ options = options.merge({ retries: 0 })
668
+ end
447
669
  execute do
448
670
  service.delete_object_access_control \
449
671
  bucket_name, file_name, entity,
450
- generation: generation, user_project: user_project(user_project)
672
+ generation: generation, user_project: user_project(user_project),
673
+ options: options
674
+ end
675
+ end
676
+
677
+ ##
678
+ # Creates a new HMAC key for the specified service account.
679
+ # Returns Google::Apis::StorageV1::HmacKey.
680
+ def create_hmac_key service_account_email, project_id: nil,
681
+ user_project: nil, options: {}
682
+
683
+ if options[:retries].nil?
684
+ options = options.merge({ retries: 0 })
685
+ end
686
+
687
+ execute do
688
+ service.create_project_hmac_key \
689
+ (project_id || @project), service_account_email,
690
+ user_project: user_project(user_project),
691
+ options: options
692
+ end
693
+ end
694
+
695
+ ##
696
+ # Deletes an HMAC key. Key must be in the INACTIVE state.
697
+ def delete_hmac_key access_id, project_id: nil, user_project: nil,
698
+ options: {}
699
+ execute do
700
+ service.delete_project_hmac_key \
701
+ (project_id || @project), access_id,
702
+ user_project: user_project(user_project),
703
+ options: options
704
+ end
705
+ end
706
+
707
+ ##
708
+ # Retrieves an HMAC key's metadata.
709
+ # Returns Google::Apis::StorageV1::HmacKeyMetadata.
710
+ def get_hmac_key access_id, project_id: nil, user_project: nil,
711
+ options: {}
712
+ execute do
713
+ service.get_project_hmac_key \
714
+ (project_id || @project), access_id,
715
+ user_project: user_project(user_project),
716
+ options: options
717
+ end
718
+ end
719
+
720
+ ##
721
+ # Retrieves a list of HMAC key metadata matching the criteria.
722
+ # Returns Google::Apis::StorageV1::HmacKeysMetadata.
723
+ def list_hmac_keys max: nil, token: nil, service_account_email: nil,
724
+ project_id: nil, show_deleted_keys: nil,
725
+ user_project: nil, options: {}
726
+ execute do
727
+ service.list_project_hmac_keys \
728
+ (project_id || @project),
729
+ max_results: max, page_token: token,
730
+ service_account_email: service_account_email,
731
+ show_deleted_keys: show_deleted_keys,
732
+ user_project: user_project(user_project),
733
+ options: options
734
+ end
735
+ end
736
+
737
+ ##
738
+ # Updates the state of an HMAC key. See the HMAC Key resource descriptor
739
+ # for valid states.
740
+ # Returns Google::Apis::StorageV1::HmacKeyMetadata.
741
+ def update_hmac_key access_id, hmac_key_metadata_object,
742
+ project_id: nil, user_project: nil,
743
+ options: {}
744
+ execute do
745
+ service.update_project_hmac_key \
746
+ (project_id || @project), access_id, hmac_key_metadata_object,
747
+ user_project: user_project(user_project),
748
+ options: options
749
+ end
750
+ end
751
+
752
+ ##
753
+ # Updates a bucket, including its ACL metadata.
754
+ def update_bucket bucket_name,
755
+ bucket_gapi = nil,
756
+ predefined_acl: nil,
757
+ predefined_default_acl: nil,
758
+ if_metageneration_match: nil,
759
+ if_metageneration_not_match: nil,
760
+ user_project: nil,
761
+ options: {}
762
+ bucket_gapi ||= Google::Apis::StorageV1::Bucket.new
763
+ bucket_gapi.acl = [] if predefined_acl
764
+ bucket_gapi.default_object_acl = [] if predefined_default_acl
765
+
766
+ if options[:retries].nil?
767
+ is_idempotent = retry? if_metageneration_match: if_metageneration_match
768
+ options = is_idempotent ? {} : { retries: 0 }
769
+ end
770
+
771
+ execute do
772
+ service.update_bucket bucket_name,
773
+ bucket_gapi,
774
+ predefined_acl: predefined_acl,
775
+ predefined_default_object_acl: predefined_default_acl,
776
+ if_metageneration_match: if_metageneration_match,
777
+ if_metageneration_not_match: if_metageneration_not_match,
778
+ user_project: user_project(user_project),
779
+ options: options
451
780
  end
452
781
  end
453
782
 
@@ -455,7 +784,9 @@ module Google
455
784
  # Retrieves the mime-type for a file path.
456
785
  # An empty string is returned if no mime-type can be found.
457
786
  def mime_type_for path
458
- MIME::Types.of(path).first.to_s
787
+ mime_type = MiniMime.lookup_by_filename path
788
+ return "" if mime_type.nil?
789
+ mime_type.content_type
459
790
  end
460
791
 
461
792
  # @private
@@ -551,8 +882,8 @@ module Google
551
882
  "false" => "NONE" }[str_or_bool.to_s.downcase]
552
883
  end
553
884
 
554
- def compose_file_source_objects source_files
555
- source_files.map do |file|
885
+ def compose_file_source_objects source_files, if_source_generation_match
886
+ source_objects = source_files.map do |file|
556
887
  if file.is_a? Google::Cloud::Storage::File
557
888
  Google::Apis::StorageV1::ComposeRequest::SourceObject.new \
558
889
  name: file.name,
@@ -562,6 +893,18 @@ module Google
562
893
  name: file
563
894
  end
564
895
  end
896
+ return source_objects unless if_source_generation_match
897
+ if source_files.count != if_source_generation_match.count
898
+ raise ArgumentError, "if provided, if_source_generation_match length must match sources length"
899
+ end
900
+ if_source_generation_match.each_with_index do |generation, i|
901
+ next unless generation
902
+ object_preconditions = Google::Apis::StorageV1::ComposeRequest::SourceObject::ObjectPreconditions.new(
903
+ if_generation_match: generation
904
+ )
905
+ source_objects[i].object_preconditions = object_preconditions
906
+ end
907
+ source_objects
565
908
  end
566
909
 
567
910
  def execute
@@ -569,163 +912,11 @@ module Google
569
912
  rescue Google::Apis::Error => e
570
913
  raise Google::Cloud::Error.from_error(e)
571
914
  end
572
- end
573
- end
574
- end
575
-
576
- # rubocop:disable all
577
-
578
- # @private
579
- #
580
- # IMPORTANT: These monkey-patches of Apis::StorageV1::StorageService and
581
- # Apis::Core::DownloadCommand must be verified and updated (if needed) for
582
- # every upgrade of google-api-client.
583
- #
584
- # The purpose of these modifications is to provide access to response headers
585
- # (in particular, the Content-Encoding header) for the #download_file method,
586
- # above. If google-api-client is modified to expose response headers to its
587
- # clients, this code should be removed, and #download_file updated to use that
588
- # solution instead.
589
- #
590
- module Apis
591
- # @private
592
- module StorageV1
593
- # @private
594
- class StorageService
595
- # Returns a two-element array containing:
596
- # * The `result` that is the usual return type of #get_object.
597
- # * The `http_resp` from DownloadCommand#execute_once.
598
- def get_object_with_response(bucket, object, generation: nil, if_generation_match: nil, if_generation_not_match: nil, if_metageneration_match: nil, if_metageneration_not_match: nil, projection: nil, user_project: nil, fields: nil, quota_user: nil, user_ip: nil, download_dest: nil, options: nil, &block)
599
- if download_dest.nil?
600
- command = make_simple_command(:get, 'b/{bucket}/o/{object}', options)
601
- else
602
- command = make_download_command(:get, 'b/{bucket}/o/{object}', options)
603
- command.download_dest = download_dest
604
- end
605
- command.response_representation = Google::Apis::StorageV1::Object::Representation
606
- command.response_class = Google::Apis::StorageV1::Object
607
- command.params['bucket'] = bucket unless bucket.nil?
608
- command.params['object'] = object unless object.nil?
609
- command.query['generation'] = generation unless generation.nil?
610
- command.query['ifGenerationMatch'] = if_generation_match unless if_generation_match.nil?
611
- command.query['ifGenerationNotMatch'] = if_generation_not_match unless if_generation_not_match.nil?
612
- command.query['ifMetagenerationMatch'] = if_metageneration_match unless if_metageneration_match.nil?
613
- command.query['ifMetagenerationNotMatch'] = if_metageneration_not_match unless if_metageneration_not_match.nil?
614
- command.query['projection'] = projection unless projection.nil?
615
- command.query['userProject'] = user_project unless user_project.nil?
616
- command.query['fields'] = fields unless fields.nil?
617
- command.query['quotaUser'] = quota_user unless quota_user.nil?
618
- command.query['userIp'] = user_ip unless user_ip.nil?
619
- execute_or_queue_command_with_response(command, &block)
620
- end
621
915
 
622
- # Returns a two-element array containing:
623
- # * The `result` that is the usual return type of #execute_or_queue_command.
624
- # * The `http_resp` from DownloadCommand#execute_once.
625
- def execute_or_queue_command_with_response(command, &callback)
626
- batch_command = current_batch
627
- if batch_command
628
- raise "Can not combine services in a batch" if Thread.current[:google_api_batch_service] != self
629
- batch_command.add(command, &callback)
630
- nil
631
- else
632
- command.execute_with_response(client, &callback)
633
- end
634
- end
635
- end
636
- end
637
- # @private
638
- module Core
639
- # @private
640
- # Streaming/resumable media download support
641
- class DownloadCommand < ApiCommand
642
- # Returns a two-element array containing:
643
- # * The `result` that is the usual return type of #execute.
644
- # * The `http_resp` from #execute_once.
645
- def execute_with_response(client)
646
- prepare!
647
- begin
648
- Retriable.retriable tries: options.retries + 1,
649
- base_interval: 1,
650
- multiplier: 2,
651
- on: RETRIABLE_ERRORS do |try|
652
- # This 2nd level retriable only catches auth errors, and supports 1 retry, which allows
653
- # auth to be re-attempted without having to retry all sorts of other failures like
654
- # NotFound, etc
655
- auth_tries = (try == 1 && authorization_refreshable? ? 2 : 1)
656
- Retriable.retriable tries: auth_tries,
657
- on: [Google::Apis::AuthorizationError, Signet::AuthorizationError],
658
- on_retry: proc { |*| refresh_authorization } do
659
- execute_once_with_response(client).tap do |result|
660
- if block_given?
661
- yield result, nil
662
- end
663
- end
664
- end
665
- end
666
- rescue => e
667
- if block_given?
668
- yield nil, e
669
- else
670
- raise e
671
- end
672
- end
673
- ensure
674
- release!
675
- end
676
-
677
- # Returns a two-element array containing:
678
- # * The `result` that is the usual return type of #execute_once.
679
- # * The `http_resp`.
680
- def execute_once_with_response(client, &block)
681
- request_header = header.dup
682
- apply_request_options(request_header)
683
- download_offset = nil
684
-
685
- if @offset > 0
686
- logger.debug { sprintf('Resuming download from offset %d', @offset) }
687
- request_header[RANGE_HEADER] = sprintf('bytes=%d-', @offset)
688
- end
689
-
690
- http_res = client.get(url.to_s,
691
- query: query,
692
- header: request_header,
693
- follow_redirect: true) do |res, chunk|
694
- status = res.http_header.status_code.to_i
695
- next unless OK_STATUS.include?(status)
696
-
697
- download_offset ||= (status == 206 ? @offset : 0)
698
- download_offset += chunk.bytesize
699
-
700
- if download_offset - chunk.bytesize == @offset
701
- next_chunk = chunk
702
- else
703
- # Oh no! Requested a chunk, but received the entire content
704
- chunk_index = @offset - (download_offset - chunk.bytesize)
705
- next_chunk = chunk.byteslice(chunk_index..-1)
706
- next if next_chunk.nil?
707
- end
708
- # logger.debug { sprintf('Writing chunk (%d bytes, %d total)', chunk.length, bytes_read) }
709
- @download_io.write(next_chunk)
710
-
711
- @offset += next_chunk.bytesize
712
- end
713
-
714
- @download_io.flush
715
-
716
- if @close_io_on_finish
717
- result = nil
718
- else
719
- result = @download_io
720
- end
721
- check_status(http_res.status.to_i, http_res.header, http_res.body)
722
- success([result, http_res], &block)
723
- rescue => e
724
- @download_io.flush
725
- error(e, rethrow: true, &block)
916
+ def retry? query_params
917
+ query_params.any? { |_key, val| !val.nil? }
726
918
  end
727
919
  end
728
920
  end
729
921
  end
730
- # rubocop:enable all
731
922
  end