Skip to content

Commit 6a3ab69

Browse files
feat(storage): Adding support for partial success bucket list (#32173)
1 parent 873929f commit 6a3ab69

File tree

6 files changed

+178
-30
lines changed

6 files changed

+178
-30
lines changed

google-cloud-storage/lib/google/cloud/storage/bucket/list.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ class List < DelegateClass(::Array)
2727
# that match the request and this value should be passed to
2828
# the next {Google::Cloud::Storage::Project#buckets} to continue.
2929
attr_accessor :token
30+
##
31+
# Provides a list of bucket names that are unreachable.
32+
#
33+
# This is only populated when `return_partial_success` is set to `true`
34+
# in the call to {Google::Cloud::Storage::Project#buckets}.
35+
#
36+
# @return [Array<String>]
37+
attr_reader :unreachable
3038

3139
##
3240
# @private Create a new Bucket::List with an array of values.
@@ -147,7 +155,7 @@ def all request_limit: nil, &block
147155
# @private New Bucket::List from a Google API Client
148156
# Google::Apis::StorageV1::Buckets object.
149157
def self.from_gapi gapi_list, service, prefix = nil, max = nil,
150-
user_project: nil, soft_deleted: nil
158+
user_project: nil, soft_deleted: nil, return_partial_success: nil
151159
buckets = new(Array(gapi_list.items).map do |gapi_object|
152160
Bucket.from_gapi gapi_object, service, user_project: user_project
153161
end)
@@ -157,6 +165,7 @@ def self.from_gapi gapi_list, service, prefix = nil, max = nil,
157165
buckets.instance_variable_set :@max, max
158166
buckets.instance_variable_set :@user_project, user_project
159167
buckets.instance_variable_set :@soft_deleted, soft_deleted
168+
buckets.instance_variable_set :@unreachable, Array(gapi_list.unreachable) if return_partial_success
160169
buckets
161170
end
162171

google-cloud-storage/lib/google/cloud/storage/project.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ def add_custom_header header_name, header_value
158158
# bucket instances and their files.
159159
#
160160
# See also {Bucket#requester_pays=} and {Bucket#requester_pays}.
161+
# @param [Boolean] return_partial_success
162+
# If true, the response will contain a list of unreachable buckets.
163+
# If false, ListBuckets will throw an error if there are any unreachable buckets.
161164
#
162165
# @return [Array<Google::Cloud::Storage::Bucket>] (See
163166
# {Google::Cloud::Storage::Bucket::List})
@@ -201,11 +204,22 @@ def add_custom_header header_name, header_value
201204
# soft_deleted_buckets.each do |bucket|
202205
# puts bucket.name
203206
# end
204-
def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil
207+
# @example Retrieve list of unreachable buckets
208+
# require "google/cloud/storage"
209+
#
210+
# storage = Google::Cloud::Storage.new
211+
#
212+
# buckets = storage.buckets return_partial_success: true
213+
# buckets.unreachable.each do |unreachable_bucket_name|
214+
# puts unreachable_bucket_name
215+
# end
216+
#
217+
def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil
205218
gapi = service.list_buckets \
206-
prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted
219+
prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success
207220
Bucket::List.from_gapi \
208-
gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted
221+
gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success
222+
209223
end
210224
alias find_buckets buckets
211225

google-cloud-storage/lib/google/cloud/storage/service.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,14 @@ def project_service_account
9696

9797
##
9898
# Retrieves a list of buckets for the given project.
99-
def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, options: {}
99+
def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {}
100100
execute do
101101
service.list_buckets \
102102
@project, prefix: prefix, page_token: token, max_results: max,
103103
user_project: user_project(user_project),
104-
soft_deleted: soft_deleted, options: options
104+
soft_deleted: soft_deleted,
105+
return_partial_success: return_partial_success,
106+
options: options
105107
end
106108
end
107109

google-cloud-storage/samples/acceptance/buckets_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
require_relative "../storage_get_retention_policy"
4343
require_relative "../storage_get_uniform_bucket_level_access"
4444
require_relative "../storage_list_buckets"
45+
require_relative "../storage_list_buckets_with_partial_success"
4546
require_relative "../storage_lock_retention_policy"
4647
require_relative "../storage_remove_bucket_label"
4748
require_relative "../storage_remove_cors_configuration"
@@ -616,4 +617,22 @@
616617
assert_equal "invalid: Source and destination object names must be different.", exception.message
617618
end
618619
end
620+
621+
describe "list buckets with partial success" do
622+
it 'returns a list of bucket names if return_partial_success_flag is true' do
623+
result = list_buckets_with_partial_success return_partial_success_flag: true
624+
assert_kind_of Array, result
625+
assert result.all? { |n| n.is_a? String }, "expected all items to be String"
626+
end
627+
628+
it 'returns nil for unreachable if return_partial_success_flag is false' do
629+
result = list_buckets_with_partial_success return_partial_success_flag: false
630+
assert_nil result
631+
end
632+
633+
it 'returns nil for unreachable if return_partial_success_flag is not passed' do
634+
result = list_buckets_with_partial_success return_partial_success_flag: nil
635+
assert_nil result
636+
end
637+
end
619638
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START storage_list_buckets_partial_success]
16+
# Demonstrates listing Google Cloud Storage buckets with support for partial success.
17+
#
18+
# This method initializes a Google Cloud Storage client and requests a list of buckets.
19+
# When `return_partial_success` is true, the API will return available buckets
20+
# and a list of any buckets that were unreachable.
21+
#
22+
# @param return_partial_success_flag [Boolean] Whether to allow partial success from the API.
23+
# - true: returns the available buckets and populates `unreachable` with bucket names if any.
24+
# - false: throws an error if any buckets are unreachable.
25+
def list_buckets_with_partial_success return_partial_success_flag:
26+
require "google/cloud/storage"
27+
28+
storage = Google::Cloud::Storage.new
29+
bucket_list = storage.buckets return_partial_success: return_partial_success_flag
30+
31+
puts "Reachable buckets:"
32+
# limiting the bucket count to be printed to 10 for brevity
33+
bucket_list.take(10).each do |bucket|
34+
puts bucket.name
35+
end
36+
37+
if bucket_list.unreachable
38+
puts "\nUnreachable buckets:"
39+
# limiting the bucket count to be printed to 10 for brevity
40+
bucket_list.unreachable.take(10).each do |unreachable_bucket_name|
41+
puts unreachable_bucket_name
42+
end
43+
end
44+
end
45+
# [END storage_list_buckets_partial_success]
46+
47+
list_buckets_with_partial_success return_partial_success_flag: ARGV.shift if $PROGRAM_NAME == __FILE__

0 commit comments

Comments
 (0)