Skip to content

Commit 6c45920

Browse files
authored
feat: add support for partial success in ListBuckets for grpc (#3418)
2 parents 41ea86c + 29a453d commit 6c45920

File tree

3 files changed

+113
-21
lines changed

3 files changed

+113
-21
lines changed

java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -454,26 +454,37 @@ public Page<Bucket> list(BucketListOption... options) {
454454
Opts<BucketListOpt> opts = Opts.unwrap(options).prepend(defaultOpts).prepend(ALL_BUCKET_FIELDS);
455455
GrpcCallContext grpcCallContext =
456456
opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
457+
457458
ListBucketsRequest request =
458459
defaultProjectId
459460
.get()
460461
.listBuckets()
461462
.andThen(opts.listBucketsRequest())
462463
.apply(ListBucketsRequest.newBuilder())
463464
.build();
464-
try {
465-
GrpcCallContext merge = Utils.merge(grpcCallContext, Retrying.newCallContext());
466-
return retrier.run(
467-
retryAlgorithmManager.getFor(request),
468-
() -> storageClient.listBucketsPagedCallable().call(request, merge),
469-
resp ->
470-
new TransformingPageDecorator<>(
471-
resp.getPage(),
472-
syntaxDecoders.bucket.andThen(opts.clearBucketFields()),
473-
retrier,
474-
retryAlgorithmManager.getFor(request)));
475-
} catch (Exception e) {
476-
throw StorageException.coalesce(e);
465+
466+
if (!request.getReturnPartialSuccess()) {
467+
try {
468+
GrpcCallContext merge = Utils.merge(grpcCallContext, Retrying.newCallContext());
469+
return retrier.run(
470+
retryAlgorithmManager.getFor(request),
471+
() -> storageClient.listBucketsPagedCallable().call(request, merge),
472+
resp ->
473+
new TransformingPageDecorator<>(
474+
resp.getPage(),
475+
syntaxDecoders.bucket.andThen(opts.clearBucketFields()),
476+
retrier,
477+
retryAlgorithmManager.getFor(request)));
478+
} catch (Exception e) {
479+
throw StorageException.coalesce(e);
480+
}
481+
} else {
482+
try {
483+
com.google.storage.v2.ListBucketsResponse response = listBuckets(grpcCallContext, request);
484+
return new ListBucketsWithPartialSuccessPage(grpcCallContext, request, response, opts);
485+
} catch (Exception e) {
486+
throw StorageException.coalesce(e);
487+
}
477488
}
478489
}
479490

@@ -1619,6 +1630,78 @@ public Iterable<Blob> getValues() {
16191630
}
16201631
}
16211632

1633+
private final class ListBucketsWithPartialSuccessPage implements Page<Bucket> {
1634+
1635+
private final GrpcCallContext ctx;
1636+
private final ListBucketsRequest req;
1637+
private final com.google.storage.v2.ListBucketsResponse resp;
1638+
private final Opts<BucketListOpt> opts;
1639+
1640+
private ListBucketsWithPartialSuccessPage(
1641+
GrpcCallContext ctx,
1642+
ListBucketsRequest req,
1643+
com.google.storage.v2.ListBucketsResponse resp,
1644+
Opts<BucketListOpt> opts) {
1645+
this.ctx = ctx;
1646+
this.req = req;
1647+
this.resp = resp;
1648+
this.opts = opts;
1649+
}
1650+
1651+
@Override
1652+
public boolean hasNextPage() {
1653+
return !resp.getNextPageToken().isEmpty();
1654+
}
1655+
1656+
@Override
1657+
public String getNextPageToken() {
1658+
return resp.getNextPageToken();
1659+
}
1660+
1661+
@Override
1662+
public Page<Bucket> getNextPage() {
1663+
if (!hasNextPage()) {
1664+
return null;
1665+
}
1666+
ListBucketsRequest nextPageReq =
1667+
req.toBuilder().setPageToken(resp.getNextPageToken()).build();
1668+
try {
1669+
com.google.storage.v2.ListBucketsResponse nextPageResp = listBuckets(ctx, nextPageReq);
1670+
return new ListBucketsWithPartialSuccessPage(ctx, nextPageReq, nextPageResp, opts);
1671+
} catch (Exception e) {
1672+
throw StorageException.coalesce(e);
1673+
}
1674+
}
1675+
1676+
@Override
1677+
public Iterable<Bucket> getValues() {
1678+
Decoder<com.google.storage.v2.Bucket, Bucket> bucketDecoder =
1679+
syntaxDecoders.bucket.andThen(opts.clearBucketFields());
1680+
Stream<Bucket> reachable = resp.getBucketsList().stream().map(bucketDecoder::decode);
1681+
Stream<Bucket> unreachable =
1682+
resp.getUnreachableList().stream()
1683+
.map(
1684+
name -> {
1685+
String decoded = bucketNameCodec.decode(name);
1686+
return BucketInfo.newBuilder(decoded)
1687+
.setIsUnreachable(true)
1688+
.build()
1689+
.asBucket(GrpcStorageImpl.this);
1690+
});
1691+
return Streams.concat(reachable, unreachable).collect(ImmutableList.toImmutableList());
1692+
}
1693+
1694+
@Override
1695+
public Iterable<Bucket> iterateAll() {
1696+
Page<Bucket> curr = this;
1697+
return () ->
1698+
streamIterate(curr, p -> p != null && p.hasNextPage(), Page::getNextPage)
1699+
.filter(Objects::nonNull)
1700+
.flatMap(p -> StreamSupport.stream(p.getValues().spliterator(), false))
1701+
.iterator();
1702+
}
1703+
}
1704+
16221705
static final class TransformingPageDecorator<
16231706
RequestT,
16241707
ResponseT,
@@ -1858,6 +1941,15 @@ private SourceObject sourceObjectEncode(SourceBlob from) {
18581941
return to.build();
18591942
}
18601943

1944+
private com.google.storage.v2.ListBucketsResponse listBuckets(
1945+
GrpcCallContext grpcCallContext, ListBucketsRequest request) {
1946+
GrpcCallContext merge = Utils.merge(grpcCallContext, Retrying.newCallContext());
1947+
return retrier.run(
1948+
retryAlgorithmManager.getFor(request),
1949+
() -> storageClient.listBucketsCallable().call(request, merge),
1950+
Decoder.identity());
1951+
}
1952+
18611953
private com.google.storage.v2.Bucket getBucketWithDefaultAcls(String bucketName) {
18621954
Fields fields =
18631955
UnifiedOpts.fields(

java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,17 +2470,17 @@ public static BucketListOption pageSize(long pageSize) {
24702470
return new BucketListOption(UnifiedOpts.pageSize(pageSize));
24712471
}
24722472

2473+
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
2474+
public static BucketListOption returnPartialSuccess(boolean returnPartialSuccess) {
2475+
return new BucketListOption(UnifiedOpts.returnPartialSuccess(returnPartialSuccess));
2476+
}
2477+
24732478
/** Returns an option to specify the page token from which to start listing buckets. */
24742479
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
24752480
public static BucketListOption pageToken(@NonNull String pageToken) {
24762481
return new BucketListOption(UnifiedOpts.pageToken(pageToken));
24772482
}
24782483

2479-
@TransportCompatibility({Transport.HTTP})
2480-
public static BucketListOption returnPartialSuccess(boolean returnPartialSuccess) {
2481-
return new BucketListOption(UnifiedOpts.returnPartialSuccess(returnPartialSuccess));
2482-
}
2483-
24842484
/**
24852485
* Returns an option to set a prefix to filter results to buckets whose names begin with this
24862486
* prefix.

java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITListBucketTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@
4040
@RunWith(StorageITRunner.class)
4141
@CrossRun(
4242
backends = {Backend.TEST_BENCH},
43-
transports = {Transport.HTTP})
43+
transports = {Transport.HTTP, Transport.GRPC})
4444
public class ITListBucketTest {
4545
@Inject public Storage storage;
4646

4747
@Inject public BucketInfo defaultBucket;
4848

49+
@Inject public Generator generator;
50+
4951
@Inject
5052
@BucketFixture(BucketType.HNS)
5153
public BucketInfo hnsBucket;
5254

53-
@Inject public Generator generator;
54-
5555
private static final String UNREACHABLE_BUCKET_SUFFIX = ".unreachable";
5656

5757
@Test

0 commit comments

Comments
 (0)