From e4a5f26666771ebf0edc6f1b47eb994d119fc246 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 7 Nov 2025 14:27:08 +0000 Subject: [PATCH 01/13] WIP --- .../lib/google/cloud/storage/bucket/list.rb | 4 ++- .../lib/google/cloud/storage/project.rb | 23 ++++++++++++---- .../lib/google/cloud/storage/service.rb | 5 ++-- .../storage_list_unreachable_buckets.rb | 27 +++++++++++++++++++ 4 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 google-cloud-storage/samples/storage_list_unreachable_buckets.rb diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 5ed6cf39271b..15f8425f459b 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -27,6 +27,7 @@ class List < DelegateClass(::Array) # that match the request and this value should be passed to # the next {Google::Cloud::Storage::Project#buckets} to continue. attr_accessor :token + attr_reader :unreachable ## # @private Create a new Bucket::List with an array of values. @@ -147,7 +148,7 @@ def all request_limit: nil, &block # @private New Bucket::List from a Google API Client # Google::Apis::StorageV1::Buckets object. def self.from_gapi gapi_list, service, prefix = nil, max = nil, - user_project: nil, soft_deleted: nil + user_project: nil, soft_deleted: nil, return_partial_success: false buckets = new(Array(gapi_list.items).map do |gapi_object| Bucket.from_gapi gapi_object, service, user_project: user_project end) @@ -157,6 +158,7 @@ def self.from_gapi gapi_list, service, prefix = nil, max = nil, buckets.instance_variable_set :@max, max buckets.instance_variable_set :@user_project, user_project buckets.instance_variable_set :@soft_deleted, soft_deleted + buckets.instance_variable_set :@unreachable, Array(gapi_list.unreachable) if return_partial_success buckets end diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 74c915acf5a8..0c353c548189 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -158,6 +158,8 @@ def add_custom_header header_name, header_value # bucket instances and their files. # # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. + # @param [Boolean] return_partial_success If true, retrieves the list of + # buckets that could not be reached. # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) @@ -201,11 +203,22 @@ def add_custom_header header_name, header_value # soft_deleted_buckets.each do |bucket| # puts bucket.name # end - def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil - gapi = service.list_buckets \ - prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted - Bucket::List.from_gapi \ - gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted + # @example Retrieve list of unreachable buckets + # require "google/cloud/storage" + # + # storage = Google::Cloud::Storage.new + # + # buckets = storage.buckets return_partial_success: true + # buckets.unreachable.each do |bucket| + # puts bucket + # end + # + def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: false + gapi = service.list_buckets \ + prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + Bucket::List.from_gapi \ + gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + end alias find_buckets buckets diff --git a/google-cloud-storage/lib/google/cloud/storage/service.rb b/google-cloud-storage/lib/google/cloud/storage/service.rb index d9adf2acff65..a9c188f4939c 100644 --- a/google-cloud-storage/lib/google/cloud/storage/service.rb +++ b/google-cloud-storage/lib/google/cloud/storage/service.rb @@ -96,12 +96,13 @@ def project_service_account ## # Retrieves a list of buckets for the given project. - def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, options: {} + def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} execute do service.list_buckets \ @project, prefix: prefix, page_token: token, max_results: max, user_project: user_project(user_project), - soft_deleted: soft_deleted, options: options + soft_deleted: soft_deleted, + return_partial_success: return_partial_success, options: options end end diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb new file mode 100644 index 000000000000..89e66c25a7bb --- /dev/null +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -0,0 +1,27 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START storage_list_unreachable_buckets] +def list_unreachable_buckets + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket_list = storage.buckets(return_partial_success: true) + bucket_list.unreachable.each do |bucket| + puts bucket + end +end +# [END storage_list_unreachable_buckets] + +list_unreachable_buckets if $PROGRAM_NAME == __FILE__ From f886ee59f283a5d1b7314cc755f8d8a8a438fb7c Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Tue, 11 Nov 2025 12:09:15 +0000 Subject: [PATCH 02/13] adding unit test case --- .../test/google/cloud/storage/project_test.rb | 105 ++++++++++++++---- 1 file changed, 81 insertions(+), 24 deletions(-) diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index b7e2fd7c0269..22ff1e4e2914 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -615,7 +615,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -633,7 +633,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -650,7 +650,8 @@ def stub.insert_bucket *args mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(num_buckets,"next_page_token",soft_deleted), [project], prefix: nil, page_token: nil, -max_results: nil, user_project: nil, soft_deleted: true, options: {} + max_results: nil, user_project: nil, soft_deleted: true, + return_partial_success: false, options: {} storage.service.mocked_service = mock buckets = storage.buckets soft_deleted: true @@ -667,8 +668,8 @@ def stub.insert_bucket *args it "paginates buckets" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -687,8 +688,8 @@ def stub.insert_bucket *args it "paginates buckets with max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -709,7 +710,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -726,8 +727,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -745,8 +746,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -764,8 +765,8 @@ def stub.insert_bucket *args it "paginates buckets with all" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -778,8 +779,8 @@ def stub.insert_bucket *args it "paginates buckets with all and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -792,8 +793,8 @@ def stub.insert_bucket *args it "iterates buckets with all using Enumerator" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -806,8 +807,8 @@ def stub.insert_bucket *args it "iterates buckets with all and request_limit set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -820,8 +821,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to true" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -835,8 +836,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to another project ID" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock @@ -1041,6 +1042,54 @@ def stub.insert_bucket *args _(bucket).must_be :lazy? end + it "Lists the unreachable buckets if return_partial_success is true" do + unreachable_buckets = ["projects/_/buckets/bucket1", + "projects/_/buckets/bucket2", + "projects/_/buckets/bucket3"] + + mock = Minitest::Mock.new + + mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, options: {} + + storage.service.mocked_service = mock + + buckets = storage.buckets(return_partial_success: true) + + mock.verify + + _(buckets.unreachable).must_equal unreachable_buckets + end + + it "returns empty array for unreachable buckets if return_partial_success is true and unreachable bucket list is empty" do + unreachable_buckets = [] + + mock = Minitest::Mock.new + + mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, options: {} + + storage.service.mocked_service = mock + + buckets = storage.buckets(return_partial_success: true) + + mock.verify + + _(buckets.unreachable).must_equal unreachable_buckets + end + + it "returns nil array for unreachable buckets if return_partial_success is not passed" do + + mock = Minitest::Mock.new + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + + storage.service.mocked_service = mock + + buckets = storage.buckets + + mock.verify + + _(buckets.unreachable).must_be :nil? + end + def bucket_with_location created_bucket, location_type: bucket_location_type resp_bucket = created_bucket.dup @@ -1081,6 +1130,14 @@ def list_buckets_gapi count = 2, token = nil, soft_deleted = nil ) end + def list_unreachable_buckets_gapi count = 2, token = nil, soft_deleted = nil, unreachable = [] + buckets = count.times.map { Google::Apis::StorageV1::Bucket.from_json random_bucket_hash(soft_deleted: soft_deleted).to_json } + buckets_obj = Google::Apis::StorageV1::Buckets.new( + kind: "storage#buckets", items: buckets, next_page_token: token, unreachable: unreachable + ) + buckets_obj + end + def restored_bucket_gapi name Google::Apis::StorageV1::Bucket.from_json random_bucket_hash(name: name).to_json end From d157f246e4bc8a5b64853490f3e26d4b019cd072 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Tue, 11 Nov 2025 12:24:09 +0000 Subject: [PATCH 03/13] add yard doc --- .../lib/google/cloud/storage/bucket/list.rb | 7 +++++++ google-cloud-storage/lib/google/cloud/storage/project.rb | 8 ++++---- google-cloud-storage/lib/google/cloud/storage/service.rb | 3 ++- .../samples/storage_list_unreachable_buckets.rb | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 15f8425f459b..9780ae2eb518 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -27,6 +27,13 @@ class List < DelegateClass(::Array) # that match the request and this value should be passed to # the next {Google::Cloud::Storage::Project#buckets} to continue. attr_accessor :token + ## + # The list of buckets that could not be reached. + # + # This is only populated when `return_partial_success` is set to `true` + # in the call to {Google::Cloud::Storage::Project#buckets}. + # + # @return [Array] attr_reader :unreachable ## diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 0c353c548189..ce0ef0ad0219 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -214,10 +214,10 @@ def add_custom_header header_name, header_value # end # def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: false - gapi = service.list_buckets \ - prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success - Bucket::List.from_gapi \ - gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + gapi = service.list_buckets \ + prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + Bucket::List.from_gapi \ + gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success end alias find_buckets buckets diff --git a/google-cloud-storage/lib/google/cloud/storage/service.rb b/google-cloud-storage/lib/google/cloud/storage/service.rb index a9c188f4939c..957a6ce28e6d 100644 --- a/google-cloud-storage/lib/google/cloud/storage/service.rb +++ b/google-cloud-storage/lib/google/cloud/storage/service.rb @@ -102,7 +102,8 @@ def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_dele @project, prefix: prefix, page_token: token, max_results: max, user_project: user_project(user_project), soft_deleted: soft_deleted, - return_partial_success: return_partial_success, options: options + return_partial_success: return_partial_success, + options: options end end diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index 89e66c25a7bb..438a763cc35c 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -16,7 +16,7 @@ def list_unreachable_buckets require "google/cloud/storage" - storage = Google::Cloud::Storage.new + storage = Google::Cloud::Storage.new bucket_list = storage.buckets(return_partial_success: true) bucket_list.unreachable.each do |bucket| puts bucket From ec65aa9a936d9747195a4e7c8377e3e3055b0a1a Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Tue, 11 Nov 2025 12:27:22 +0000 Subject: [PATCH 04/13] adding doc --- .../storage_list_unreachable_buckets.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index 438a763cc35c..10da204f8fbd 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -13,6 +13,25 @@ # limitations under the License. # [START storage_list_unreachable_buckets] +# Lists and prints buckets that were reported as unreachable when requesting +# a (possibly partial) list of buckets from Google Cloud Storage. +# +# This helper enables partial success when listing buckets by setting +# `return_partial_success: true` on the API call. When the server returns +# partial results, the returned collection exposes an `unreachable` list +# containing the names (or identifiers) of buckets that could not be +# retrieved in the request. This method iterates over that list and writes +# each unreachable bucket to STDOUT. +# +# Behavior: +# - Initializes a Storage client. +# - Uses `Storage#buckets(return_partial_success: true)` to request buckets. +# - Prints one unreachable bucket per line to standard output. +# +# Example: +# # Ensure credentials and project are configured, then call: +# list_unreachable_buckets + def list_unreachable_buckets require "google/cloud/storage" From 247c4438a0865591570131e0a4147df8a6c051af Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Wed, 12 Nov 2025 08:29:37 +0000 Subject: [PATCH 05/13] setting default as nil --- .../lib/google/cloud/storage/project.rb | 2 +- .../lib/google/cloud/storage/service.rb | 2 +- .../test/google/cloud/storage/project_test.rb | 52 +++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index ce0ef0ad0219..ae5678dd2fe9 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -213,7 +213,7 @@ def add_custom_header header_name, header_value # puts bucket # end # - def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: false + def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil gapi = service.list_buckets \ prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success Bucket::List.from_gapi \ diff --git a/google-cloud-storage/lib/google/cloud/storage/service.rb b/google-cloud-storage/lib/google/cloud/storage/service.rb index 957a6ce28e6d..df61e682f8e0 100644 --- a/google-cloud-storage/lib/google/cloud/storage/service.rb +++ b/google-cloud-storage/lib/google/cloud/storage/service.rb @@ -96,7 +96,7 @@ def project_service_account ## # Retrieves a list of buckets for the given project. - def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} execute do service.list_buckets \ @project, prefix: prefix, page_token: token, max_results: max, diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index 22ff1e4e2914..05724226ca34 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -615,7 +615,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -633,7 +633,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -651,7 +651,7 @@ def stub.insert_bucket *args mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(num_buckets,"next_page_token",soft_deleted), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: true, - return_partial_success: false, options: {} + return_partial_success: nil, options: {} storage.service.mocked_service = mock buckets = storage.buckets soft_deleted: true @@ -668,8 +668,8 @@ def stub.insert_bucket *args it "paginates buckets" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -688,8 +688,8 @@ def stub.insert_bucket *args it "paginates buckets with max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -710,7 +710,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -727,8 +727,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -746,8 +746,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -765,8 +765,8 @@ def stub.insert_bucket *args it "paginates buckets with all" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -779,8 +779,8 @@ def stub.insert_bucket *args it "paginates buckets with all and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -793,8 +793,8 @@ def stub.insert_bucket *args it "iterates buckets with all using Enumerator" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -807,8 +807,8 @@ def stub.insert_bucket *args it "iterates buckets with all and request_limit set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -821,8 +821,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to true" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -836,8 +836,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to another project ID" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, options: {} storage.service.mocked_service = mock @@ -1076,14 +1076,14 @@ def stub.insert_bucket *args _(buckets.unreachable).must_equal unreachable_buckets end - it "returns nil array for unreachable buckets if return_partial_success is not passed" do + it "returns nil array for unreachable buckets if return_partial_success is passed as false" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} storage.service.mocked_service = mock - buckets = storage.buckets + buckets = storage.buckets(return_partial_success: false) mock.verify From c405396b7e07dda5f860b17c50ac38cdd662bd0e Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Wed, 12 Nov 2025 08:32:42 +0000 Subject: [PATCH 06/13] adding region tag --- .../samples/storage_list_unreachable_buckets.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index 10da204f8fbd..545ec366136c 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# [START storage_list_unreachable_buckets] +# [START storage_list_buckets_partial_success] # Lists and prints buckets that were reported as unreachable when requesting # a (possibly partial) list of buckets from Google Cloud Storage. # @@ -41,6 +41,6 @@ def list_unreachable_buckets puts bucket end end -# [END storage_list_unreachable_buckets] +# [END storage_list_buckets_partial_success] list_unreachable_buckets if $PROGRAM_NAME == __FILE__ From 91c364ecbc00a7f8e3d99be301db8d670f95b459 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Wed, 12 Nov 2025 08:39:28 +0000 Subject: [PATCH 07/13] change default --- google-cloud-storage/lib/google/cloud/storage/bucket/list.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 9780ae2eb518..592182f76e3b 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -155,7 +155,7 @@ def all request_limit: nil, &block # @private New Bucket::List from a Google API Client # Google::Apis::StorageV1::Buckets object. def self.from_gapi gapi_list, service, prefix = nil, max = nil, - user_project: nil, soft_deleted: nil, return_partial_success: false + user_project: nil, soft_deleted: nil, return_partial_success: nil buckets = new(Array(gapi_list.items).map do |gapi_object| Bucket.from_gapi gapi_object, service, user_project: user_project end) From 2d6126c6fb9de7924732b0956ecce1d03de77176 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 14 Nov 2025 05:59:49 +0000 Subject: [PATCH 08/13] updates --- .../lib/google/cloud/storage/bucket/list.rb | 2 +- .../lib/google/cloud/storage/project.rb | 5 +++-- .../storage_list_unreachable_buckets.rb | 21 +++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 592182f76e3b..2378ed0034a9 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -28,7 +28,7 @@ class List < DelegateClass(::Array) # the next {Google::Cloud::Storage::Project#buckets} to continue. attr_accessor :token ## - # The list of buckets that could not be reached. + # Provides a list of bucket names that are unreachable. # # This is only populated when `return_partial_success` is set to `true` # in the call to {Google::Cloud::Storage::Project#buckets}. diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index ae5678dd2fe9..8c12454ee513 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -160,6 +160,7 @@ def add_custom_header header_name, header_value # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. # @param [Boolean] return_partial_success If true, retrieves the list of # buckets that could not be reached. + # @param [Boolean] return_partial_success If false, returns unreachable as nil # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) @@ -209,8 +210,8 @@ def add_custom_header header_name, header_value # storage = Google::Cloud::Storage.new # # buckets = storage.buckets return_partial_success: true - # buckets.unreachable.each do |bucket| - # puts bucket + # buckets.unreachable.each do |bucket_name| + # puts bucket_name # end # def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index 545ec366136c..e300da54e799 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -30,17 +30,26 @@ # # Example: # # Ensure credentials and project are configured, then call: -# list_unreachable_buckets +# list_buckets_with_partial_success -def list_unreachable_buckets +def list_buckets_with_partial_success require "google/cloud/storage" storage = Google::Cloud::Storage.new - bucket_list = storage.buckets(return_partial_success: true) - bucket_list.unreachable.each do |bucket| - puts bucket + bucket_list = storage.buckets return_partial_success: true + + puts "Bucket Names:" + + bucket_list.each do |bucket| + puts bucket.name end + + puts "Unreachable bucket names:" + + bucket_list.unreachable.each do |bucket_name| + puts bucket_name + end end # [END storage_list_buckets_partial_success] -list_unreachable_buckets if $PROGRAM_NAME == __FILE__ +list_buckets_with_partial_success if $PROGRAM_NAME == __FILE__ From 5e5ec769db9de3c4fa7bc84345efd32bfbef3742 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 14 Nov 2025 06:03:18 +0000 Subject: [PATCH 09/13] updates --- google-cloud-storage/lib/google/cloud/storage/project.rb | 4 ++-- .../samples/storage_list_unreachable_buckets.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 8c12454ee513..e68ef5c16973 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -210,8 +210,8 @@ def add_custom_header header_name, header_value # storage = Google::Cloud::Storage.new # # buckets = storage.buckets return_partial_success: true - # buckets.unreachable.each do |bucket_name| - # puts bucket_name + # buckets.unreachable.each do |unreachable_bucket_name| + # puts unreachable_bucket_name # end # def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index e300da54e799..c89f801f3c8f 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -46,8 +46,8 @@ def list_buckets_with_partial_success puts "Unreachable bucket names:" - bucket_list.unreachable.each do |bucket_name| - puts bucket_name + bucket_list.unreachable.each do |unreachable_bucket_name| + puts unreachable_bucket_name end end # [END storage_list_buckets_partial_success] From 9f3f30ec4bbc54fd7585f784278a7d2325b75269 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 14 Nov 2025 06:19:30 +0000 Subject: [PATCH 10/13] resolve yard --- .../lib/google/cloud/storage/project.rb | 6 +++--- .../samples/storage_list_unreachable_buckets.rb | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index e68ef5c16973..2fc43beba019 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -158,9 +158,9 @@ def add_custom_header header_name, header_value # bucket instances and their files. # # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. - # @param [Boolean] return_partial_success If true, retrieves the list of - # buckets that could not be reached. - # @param [Boolean] return_partial_success If false, returns unreachable as nil + # @param [Boolean] return_partial_success + # If true, retrieves the list of buckets that could not be reached. + # If false, returns unreachable as nil # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb index c89f801f3c8f..104ee0706753 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_unreachable_buckets.rb @@ -39,16 +39,14 @@ def list_buckets_with_partial_success bucket_list = storage.buckets return_partial_success: true puts "Bucket Names:" - bucket_list.each do |bucket| puts bucket.name end - puts "Unreachable bucket names:" - - bucket_list.unreachable.each do |unreachable_bucket_name| - puts unreachable_bucket_name - end + puts "Unreachable bucket names:" + bucket_list.unreachable.each do |unreachable_bucket_name| + puts unreachable_bucket_name + end end # [END storage_list_buckets_partial_success] From 8048d79aa592d95a37380e2014cd1c86fce91d84 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 21 Nov 2025 06:58:05 +0000 Subject: [PATCH 11/13] fix --- .../lib/google/cloud/storage/project.rb | 4 ++-- ...torage_list_buckets_with_partial_success.rb} | 17 +++-------------- 2 files changed, 5 insertions(+), 16 deletions(-) rename google-cloud-storage/samples/{storage_list_unreachable_buckets.rb => storage_list_buckets_with_partial_success.rb} (63%) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 2fc43beba019..c43a207b8333 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -159,8 +159,8 @@ def add_custom_header header_name, header_value # # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. # @param [Boolean] return_partial_success - # If true, retrieves the list of buckets that could not be reached. - # If false, returns unreachable as nil + # If true, returns unreachable attribute as the list of buckets with partial success enabled. + # If false, returns unreachable attribute as nil # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) diff --git a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb similarity index 63% rename from google-cloud-storage/samples/storage_list_unreachable_buckets.rb rename to google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb index 104ee0706753..25beb70b97fe 100644 --- a/google-cloud-storage/samples/storage_list_unreachable_buckets.rb +++ b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb @@ -13,24 +13,13 @@ # limitations under the License. # [START storage_list_buckets_partial_success] -# Lists and prints buckets that were reported as unreachable when requesting -# a (possibly partial) list of buckets from Google Cloud Storage. -# -# This helper enables partial success when listing buckets by setting -# `return_partial_success: true` on the API call. When the server returns -# partial results, the returned collection exposes an `unreachable` list -# containing the names (or identifiers) of buckets that could not be -# retrieved in the request. This method iterates over that list and writes -# each unreachable bucket to STDOUT. +# Demonstrates listing buckets from Google Cloud Storage with partial success enabled. # # Behavior: # - Initializes a Storage client. # - Uses `Storage#buckets(return_partial_success: true)` to request buckets. -# - Prints one unreachable bucket per line to standard output. -# -# Example: -# # Ensure credentials and project are configured, then call: -# list_buckets_with_partial_success +# - Prints one unreachable bucket name per line to standard output. + def list_buckets_with_partial_success require "google/cloud/storage" From 061a23343f3e86db5e27c94fa4e9bd205d71d55a Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Thu, 27 Nov 2025 08:05:15 +0000 Subject: [PATCH 12/13] adding test case for samples --- .../lib/google/cloud/storage/project.rb | 3 +- .../samples/acceptance/buckets_test.rb | 14 +++++++++ ...orage_list_buckets_with_partial_success.rb | 31 +++++++++---------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index c43a207b8333..f69394cb622f 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -160,7 +160,8 @@ def add_custom_header header_name, header_value # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. # @param [Boolean] return_partial_success # If true, returns unreachable attribute as the list of buckets with partial success enabled. - # If false, returns unreachable attribute as nil + # if false, ListBuckets will throw an error if there are any unreachable buckets + # and unreachable attribute is returned nil # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index 7afc71d14415..93a0e044fac6 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -42,6 +42,7 @@ require_relative "../storage_get_retention_policy" require_relative "../storage_get_uniform_bucket_level_access" require_relative "../storage_list_buckets" +require_relative "../storage_list_buckets_with_partial_success" require_relative "../storage_lock_retention_policy" require_relative "../storage_remove_bucket_label" require_relative "../storage_remove_cors_configuration" @@ -616,4 +617,17 @@ assert_equal "invalid: Source and destination object names must be different.", exception.message end end + + describe "list buckets with partial success" do + it 'returns a list of bucket names if return_partial_success_flag is true' do + result = list_buckets_with_partial_success return_partial_success_flag: true + assert_kind_of Array, result + assert result.all? { |n| n.is_a? String }, "expected all items to be String" + end + + it 'returns nil if return_partial_success_flag is false' do + result = list_buckets_with_partial_success return_partial_success_flag: false + assert_nil result + end + end end diff --git a/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb index 25beb70b97fe..268990c302b1 100644 --- a/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb +++ b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb @@ -13,30 +13,29 @@ # limitations under the License. # [START storage_list_buckets_partial_success] -# Demonstrates listing buckets from Google Cloud Storage with partial success enabled. +# Demonstrates listing Google Cloud Storage buckets with support for partial success. # -# Behavior: -# - Initializes a Storage client. -# - Uses `Storage#buckets(return_partial_success: true)` to request buckets. -# - Prints one unreachable bucket name per line to standard output. - +# This method initializes a Google Cloud Storage client and requests a list of buckets +# which are present in unreachable locations. +# If `return_partial_success_flag` is true, the Storage API will return a list of buckets which are +# unreachable in the `unreachable` field of the response. +# +# If `return_partial_success_flag` is false the method will return nil. +# +# @param return_partial_success_flag [Boolean] Whether to allow partial success from the API. +# - true: returns the available buckets and populates `unreachable` with bucket names. +# - false: the method returns nil. -def list_buckets_with_partial_success +def list_buckets_with_partial_success return_partial_success_flag: require "google/cloud/storage" storage = Google::Cloud::Storage.new - bucket_list = storage.buckets return_partial_success: true - - puts "Bucket Names:" - bucket_list.each do |bucket| - puts bucket.name - end + bucket_list = storage.buckets return_partial_success: return_partial_success_flag - puts "Unreachable bucket names:" - bucket_list.unreachable.each do |unreachable_bucket_name| + bucket_list.unreachable&.each do |unreachable_bucket_name| puts unreachable_bucket_name end end # [END storage_list_buckets_partial_success] -list_buckets_with_partial_success if $PROGRAM_NAME == __FILE__ +list_buckets_with_partial_success return_partial_success_flag: ARGV.shift if $PROGRAM_NAME == __FILE__ From d09fee19df55419c54d571cb67094ec9acdc1612 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 28 Nov 2025 08:54:38 +0000 Subject: [PATCH 13/13] fxing samples --- .../lib/google/cloud/storage/project.rb | 7 ++--- .../samples/acceptance/buckets_test.rb | 7 ++++- ...orage_list_buckets_with_partial_success.rb | 28 +++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index f69394cb622f..47e4ffc32da4 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -158,10 +158,9 @@ def add_custom_header header_name, header_value # bucket instances and their files. # # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. - # @param [Boolean] return_partial_success - # If true, returns unreachable attribute as the list of buckets with partial success enabled. - # if false, ListBuckets will throw an error if there are any unreachable buckets - # and unreachable attribute is returned nil + # @param [Boolean] return_partial_success + # If true, the response will contain a list of unreachable buckets. + # If false, ListBuckets will throw an error if there are any unreachable buckets. # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index 93a0e044fac6..bb78ba9bbe44 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -625,9 +625,14 @@ assert result.all? { |n| n.is_a? String }, "expected all items to be String" end - it 'returns nil if return_partial_success_flag is false' do + it 'returns nil for unreachable if return_partial_success_flag is false' do result = list_buckets_with_partial_success return_partial_success_flag: false assert_nil result end + + it 'returns nil for unreachable if return_partial_success_flag is not passed' do + result = list_buckets_with_partial_success return_partial_success_flag: nil + assert_nil result + end end end diff --git a/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb index 268990c302b1..c7f23469f54e 100644 --- a/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb +++ b/google-cloud-storage/samples/storage_list_buckets_with_partial_success.rb @@ -15,25 +15,31 @@ # [START storage_list_buckets_partial_success] # Demonstrates listing Google Cloud Storage buckets with support for partial success. # -# This method initializes a Google Cloud Storage client and requests a list of buckets -# which are present in unreachable locations. -# If `return_partial_success_flag` is true, the Storage API will return a list of buckets which are -# unreachable in the `unreachable` field of the response. -# -# If `return_partial_success_flag` is false the method will return nil. +# This method initializes a Google Cloud Storage client and requests a list of buckets. +# When `return_partial_success` is true, the API will return available buckets +# and a list of any buckets that were unreachable. # # @param return_partial_success_flag [Boolean] Whether to allow partial success from the API. -# - true: returns the available buckets and populates `unreachable` with bucket names. -# - false: the method returns nil. - +# - true: returns the available buckets and populates `unreachable` with bucket names if any. +# - false: throws an error if any buckets are unreachable. def list_buckets_with_partial_success return_partial_success_flag: require "google/cloud/storage" storage = Google::Cloud::Storage.new bucket_list = storage.buckets return_partial_success: return_partial_success_flag - bucket_list.unreachable&.each do |unreachable_bucket_name| - puts unreachable_bucket_name + puts "Reachable buckets:" + # limiting the bucket count to be printed to 10 for brevity + bucket_list.take(10).each do |bucket| + puts bucket.name + end + + if bucket_list.unreachable + puts "\nUnreachable buckets:" + # limiting the bucket count to be printed to 10 for brevity + bucket_list.unreachable.take(10).each do |unreachable_bucket_name| + puts unreachable_bucket_name + end end end # [END storage_list_buckets_partial_success]