From 330e795040592e5df22d44fb5216ad7cf2448e81 Mon Sep 17 00:00:00 2001 From: BenWhitehead Date: Wed, 2 Aug 2023 17:40:25 -0400 Subject: [PATCH] fix: update grpc default metadata projection to include acl same as json (#2150) --- .../google/cloud/storage/GrpcStorageImpl.java | 14 +- .../storage/it/ITBlobWriteChannelTest.java | 1 - .../ITDefaultProjectionCompatibilityTest.java | 166 ++++++++++++++++++ 3 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITDefaultProjectionCompatibilityTest.java diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java index fad1f66e3..95ddad874 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java @@ -164,6 +164,11 @@ final class GrpcStorageImpl extends BaseService StandardOpenOption.TRUNCATE_EXISTING); private static final BucketSourceOption[] EMPTY_BUCKET_SOURCE_OPTIONS = new BucketSourceOption[0]; + private static final Opts ALL_BLOB_FIELDS = + Opts.from(UnifiedOpts.fields(ImmutableSet.copyOf(BlobField.values()))); + private static final Opts ALL_BUCKET_FIELDS = + Opts.from(UnifiedOpts.fields(ImmutableSet.copyOf(BucketField.values()))); + final StorageClient storageClient; final WriterFactory writerFactory; final GrpcConversions codecs; @@ -420,7 +425,7 @@ public Blob get(BlobId blob) { @Override public Page list(BucketListOption... options) { - Opts opts = Opts.unwrap(options).prepend(defaultOpts); + Opts opts = Opts.unwrap(options).prepend(defaultOpts).prepend(ALL_BUCKET_FIELDS); GrpcCallContext grpcCallContext = opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault()); ListBucketsRequest request = @@ -448,7 +453,7 @@ public Page list(BucketListOption... options) { @Override public Page list(String bucket, BlobListOption... options) { - Opts opts = Opts.unwrap(options).prepend(defaultOpts); + Opts opts = Opts.unwrap(options).prepend(defaultOpts).prepend(ALL_BLOB_FIELDS); GrpcCallContext grpcCallContext = opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault()); ListObjectsRequest.Builder builder = @@ -1958,7 +1963,8 @@ private Object updateObject(UpdateObjectRequest req) { @Nullable private Blob internalBlobGet(BlobId blob, Opts unwrap) { - Opts opts = unwrap.resolveFrom(blob).prepend(defaultOpts); + Opts opts = + unwrap.resolveFrom(blob).prepend(defaultOpts).prepend(ALL_BLOB_FIELDS); GrpcCallContext grpcCallContext = opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault()); GetObjectRequest.Builder builder = @@ -1983,7 +1989,7 @@ private Blob internalBlobGet(BlobId blob, Opts unwrap) { @Nullable private Bucket internalBucketGet(String bucket, Opts unwrap) { - Opts opts = unwrap.prepend(defaultOpts); + Opts opts = unwrap.prepend(defaultOpts).prepend(ALL_BUCKET_FIELDS); GrpcCallContext grpcCallContext = opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault()); GetBucketRequest.Builder builder = diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java index 338f2d9be..63aac4f56 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java @@ -145,7 +145,6 @@ public void testWriteChannelExistingBlob() throws IOException { @Test public void changeChunkSizeAfterWrite() throws IOException { BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build(); - System.out.println("info = " + info); int _512KiB = 512 * 1024; byte[] bytes = DataGenerator.base64Characters().genBytes(_512KiB + 13); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITDefaultProjectionCompatibilityTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITDefaultProjectionCompatibilityTest.java new file mode 100644 index 000000000..32d0f31d9 --- /dev/null +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITDefaultProjectionCompatibilityTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2023 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. + */ + +package com.google.cloud.storage.it; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.Acl.Entity; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.Storage.BlobListOption; +import com.google.cloud.storage.Storage.BucketListOption; +import com.google.cloud.storage.TransportCompatibility.Transport; +import com.google.cloud.storage.it.runner.StorageITRunner; +import com.google.cloud.storage.it.runner.annotations.Backend; +import com.google.cloud.storage.it.runner.annotations.Inject; +import com.google.cloud.storage.it.runner.annotations.SingleBackend; +import com.google.cloud.storage.it.runner.annotations.StorageFixture; +import com.google.cloud.storage.it.runner.registry.ObjectsFixture; +import com.google.common.base.MoreObjects; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(StorageITRunner.class) +@SingleBackend(Backend.PROD) +public final class ITDefaultProjectionCompatibilityTest { + + @Inject + @StorageFixture(Transport.HTTP) + public Storage http; + + @Inject + @StorageFixture(Transport.GRPC) + public Storage grpc; + + @Inject public BucketInfo bucket; + + @Inject public ObjectsFixture objectsFixture; + + @Test + public void objectMetadata_includesAcls() { + Blob httpBlob = http.get(objectsFixture.getInfo1().getBlobId()); + Blob grpcBlob = grpc.get(objectsFixture.getInfo1().getBlobId()); + + assertThat(extractFromBlob(grpcBlob)).isEqualTo(extractFromBlob(httpBlob)); + } + + @Test + public void listObjectMetadata_includesAcls() { + String bucketName = bucket.getName(); + BlobListOption prefix = BlobListOption.prefix(objectsFixture.getInfo1().getBlobId().getName()); + List httpBlob = http.list(bucketName, prefix).streamAll().collect(Collectors.toList()); + List grpcBlob = grpc.list(bucketName, prefix).streamAll().collect(Collectors.toList()); + + List a = extractFromBlobs(httpBlob); + List b = extractFromBlobs(grpcBlob); + + assertThat(a).isEqualTo(b); + } + + @Test + public void bucketMetadata_includesAcls() { + Bucket httpBucket = http.get(bucket.getName()); + Bucket grpcBucket = grpc.get(bucket.getName()); + + assertThat(extractFromBucket(httpBucket)).isEqualTo(extractFromBucket(grpcBucket)); + } + + @Test + public void listBucketMetadata_includesAcls() { + BucketListOption prefix = BucketListOption.prefix(bucket.getName()); + List httpBucket = http.list(prefix).streamAll().collect(Collectors.toList()); + List grpcBucket = grpc.list(prefix).streamAll().collect(Collectors.toList()); + + List a = extractFromBuckets(httpBucket); + List b = extractFromBuckets(grpcBucket); + + assertThat(a).isEqualTo(b); + } + + @NonNull + private static List extractFromBlobs(List httpBlob) { + return httpBlob.stream() + .map(ITDefaultProjectionCompatibilityTest::extractFromBlob) + .collect(Collectors.toList()); + } + + @NonNull + private static AclRelatedFields extractFromBlob(Blob b) { + return new AclRelatedFields(b.getOwner(), b.getAcl(), null); + } + + @NonNull + private static List extractFromBuckets(List httpBucket) { + return httpBucket.stream() + .map(ITDefaultProjectionCompatibilityTest::extractFromBucket) + .collect(Collectors.toList()); + } + + @NonNull + private static AclRelatedFields extractFromBucket(Bucket b) { + return new AclRelatedFields(b.getOwner(), b.getAcl(), null); + } + + private static final class AclRelatedFields { + @Nullable private final Entity owner; + @Nullable private final List acls; + @Nullable private final List defaultAcls; + + private AclRelatedFields( + @Nullable Entity owner, @Nullable List acls, @Nullable List defaultAcls) { + this.owner = owner; + this.acls = acls; + this.defaultAcls = defaultAcls; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AclRelatedFields)) { + return false; + } + AclRelatedFields that = (AclRelatedFields) o; + return Objects.equals(owner, that.owner) + && Objects.equals(acls, that.acls) + && Objects.equals(defaultAcls, that.defaultAcls); + } + + @Override + public int hashCode() { + return Objects.hash(owner, acls, defaultAcls); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("owner", owner) + .add("acls", acls) + .add("defaultAcls", defaultAcls) + .toString(); + } + } +}