diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java index 77cb1344a3..2dae52b8e4 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java @@ -281,6 +281,10 @@ interface ProjectAsSource { O asSource(); } + interface HasherSelector extends BucketObjectHmacKeyAllOpt { + Hasher getHasher(); + } + /** * This class extends off {@link ObjectSourceOpt} and {@link ObjectTargetOpt} in order to satisfy * some the shimming constraints of the subclasses of {@link OptionShim}. @@ -589,7 +593,11 @@ static Headers extraHeaders(ImmutableMap extraHeaders) { return new Headers(extraHeaders); } - static final class Crc32cMatch implements ObjectTargetOpt { + static DefaultHasherSelector defaultHasherSelector() { + return DefaultHasherSelector.INSTANCE; + } + + static final class Crc32cMatch implements ObjectTargetOpt, HasherSelector { private static final long serialVersionUID = 8172282701777561769L; private final int val; @@ -630,6 +638,11 @@ public Mapper bidiWriteObject() { }; } + @Override + public Hasher getHasher() { + return Hasher.noop(); + } + @Override public int hashCode() { return Objects.hash(val); @@ -1315,7 +1328,7 @@ public Mapper listObjects() { } @Deprecated - static final class Md5Match implements ObjectTargetOpt { + static final class Md5Match implements ObjectTargetOpt, HasherSelector { private static final long serialVersionUID = 5237207911268363887L; private final String val; @@ -1358,6 +1371,11 @@ public Mapper bidiWriteObject() { }; } + @Override + public Hasher getHasher() { + return Hasher.noop(); + } + @Override public int hashCode() { return Objects.hash(val); @@ -1983,8 +2001,8 @@ public Mapper listObjects() { } } - static final class UserProject extends RpcOptVal - implements BucketSourceOpt, + interface BucketObjectHmacKeyAllOpt + extends BucketSourceOpt, BucketTargetOpt, BucketListOpt, ObjectSourceOpt, @@ -1993,6 +2011,18 @@ static final class UserProject extends RpcOptVal HmacKeySourceOpt, HmacKeyTargetOpt, HmacKeyListOpt { + @Override + default Mapper rewriteObject() { + return Mapper.identity(); + } + + @Override + default Mapper moveObject() { + return Mapper.identity(); + } + } + + static final class UserProject extends RpcOptVal implements BucketObjectHmacKeyAllOpt { private static final long serialVersionUID = 3962499996741180460L; private UserProject(String val) { @@ -2004,28 +2034,10 @@ public Mapper getGrpcMetadataMapper() { return ctx -> ctx.withExtraHeaders(ImmutableMap.of("X-Goog-User-Project", ImmutableList.of(val))); } - - @Override - public Mapper rewriteObject() { - return Mapper.identity(); - } - - @Override - public Mapper moveObject() { - return Mapper.identity(); - } } static final class Headers extends RpcOptVal> - implements BucketSourceOpt, - BucketTargetOpt, - BucketListOpt, - ObjectSourceOpt, - ObjectTargetOpt, - ObjectListOpt, - HmacKeySourceOpt, - HmacKeyTargetOpt, - HmacKeyListOpt { + implements BucketObjectHmacKeyAllOpt { /** * The set of header names which are blocked from being able to be provided for an instance of @@ -2181,16 +2193,6 @@ private void copyEntries( } } } - - @Override - public Mapper rewriteObject() { - return Mapper.identity(); - } - - @Override - public Mapper moveObject() { - return Mapper.identity(); - } } static final class VersionsFilter extends RpcOptVal<@NonNull Boolean> implements ObjectListOpt { @@ -2606,6 +2608,17 @@ public String toString() { } } + static final class DefaultHasherSelector implements HasherSelector, Opt { + private static final DefaultHasherSelector INSTANCE = new DefaultHasherSelector(); + + private DefaultHasherSelector() {} + + @Override + public Hasher getHasher() { + return Hasher.defaultHasher(); + } + } + /** * Internal "collection" class to represent a set of {@link Opt}s, and to provide useful * transformations to individual mappers or to resolve any extractors providing a new instance @@ -2702,6 +2715,22 @@ Opts projectAsSource() { return Utils.mapBuild(builder); } + @VisibleForTesting + HasherSelector getHasherSelector() { + HasherSelector search = defaultHasherSelector(); + Predicate p = isInstanceOf(HasherSelector.class); + for (T opt : opts) { + if (p.test(opt)) { + search = (HasherSelector) opt; + } + } + return search; + } + + Hasher getHasher() { + return getHasherSelector().getHasher(); + } + Mapper grpcMetadataMapper() { return fuseMappers(GrpcMetadataMapper.class, GrpcMetadataMapper::getGrpcMetadataMapper); } @@ -2754,7 +2783,7 @@ Mapper readObjectRequest() { return fuseMappers(ObjectSourceOpt.class, ObjectSourceOpt::readObject); } - public Mapper bidiReadObjectRequest() { + Mapper bidiReadObjectRequest() { return fuseMappers(ObjectSourceOpt.class, ObjectSourceOpt::bidiReadObject); } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/UnifiedOptsTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/UnifiedOptsTest.java index 73d88580a4..e5a38b4365 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/UnifiedOptsTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/UnifiedOptsTest.java @@ -20,6 +20,10 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; +import com.google.cloud.storage.UnifiedOpts.Crc32cMatch; +import com.google.cloud.storage.UnifiedOpts.DefaultHasherSelector; +import com.google.cloud.storage.UnifiedOpts.HasherSelector; +import com.google.cloud.storage.UnifiedOpts.Md5Match; import com.google.cloud.storage.UnifiedOpts.ObjectSourceOpt; import com.google.cloud.storage.UnifiedOpts.ObjectTargetOpt; import com.google.cloud.storage.UnifiedOpts.Opt; @@ -146,6 +150,27 @@ public void validateFactoryMethodEnforceNonNull_storage_updateHmacKeyOption() th validateFactoryMethodEnforceNonNull(Storage.UpdateHmacKeyOption.class); } + @Test + public void getHasher_selectsLastValue() { + DefaultHasherSelector first = UnifiedOpts.defaultHasherSelector(); + Md5Match second = UnifiedOpts.md5Match("asdf"); + Crc32cMatch third = UnifiedOpts.crc32cMatch(3); + Opts hasherOpts = Opts.from(first, second, third); + + HasherSelector actual = hasherOpts.getHasherSelector(); + assertThat(actual).isSameInstanceAs(third); + } + + @Test + public void hasher_md5Match_noop() { + assertThat(UnifiedOpts.md5Match("xyz").getHasher()).isEqualTo(Hasher.noop()); + } + + @Test + public void hasher_crc32cMatch_noop() { + assertThat(UnifiedOpts.crc32cMatch(77).getHasher()).isEqualTo(Hasher.noop()); + } + @Test public void transformTo() { SecretKey key = @@ -172,8 +197,8 @@ public byte[] getEncoded() { UnifiedOpts.encryptionKey(key), // userProject implements both target and source UnifiedOpts.userProject("user-project"), - // crc32c is not a source opt or a ProjectToSource opt, it should be excluded - UnifiedOpts.crc32cMatch(1)); + // contentType is not a source opt or a ProjectToSource opt, it should be excluded + UnifiedOpts.setContentType("application/octet-stream")); Opts sourceOpts = targetOpts.transformTo(ObjectSourceOpt.class); Opts expected =