From 7a52d8f9ecf539f12d91eaa2a7386d8e7b998bf4 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 10 Sep 2025 15:05:17 +0530 Subject: [PATCH 1/6] fix: Correct diff logic for nested context updates --- .../java/com/google/cloud/storage/JsonUtils.java | 2 +- .../main/java/com/google/cloud/storage/Utils.java | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java index 50427a8cf4..75ad5846f7 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java @@ -70,7 +70,7 @@ static T getOutputJsonWithSelectedFields( .map(NamedField::getApiaryName) .collect(ImmutableSet.toImmutableSet()); try { - // The datamodel of the apiairy json representation doesn't have a common parent for all + // The datamodel of the apiary json representation doesn't have a common parent for all // field types, rather than writing a significant amount of code to handle all of these types // leverage Gson. // 1. serialize the object to it's json string diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java index d6f96b8ad4..5a3217b47b 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java @@ -299,7 +299,20 @@ static void diffMaps( if (left != null && right == null) { keys = left.keySet().stream().map(NamedField::literal); } else if (left == null && right != null) { - keys = right.keySet().stream().map(NamedField::literal).map(dec); + keys = + right.entrySet().stream() + .map( + e -> { + String key = e.getKey(); + Object value = e.getValue(); + NamedField literal = NamedField.literal(key); + + if (value == null) { + return literal; + } else { + return dec.apply(literal); + } + }); } else if (left != null && right != null) { MapDifference difference = Maps.difference(left, right); keys = From ce8d703435925caf127ad280c1f52373e1c406d5 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Thu, 11 Sep 2025 21:11:40 +0530 Subject: [PATCH 2/6] fix: Changed the diff logic to remove the dec and updated JsonUtils too handle ".value" --- .../com/google/cloud/storage/BlobInfo.java | 1 - .../com/google/cloud/storage/JsonUtils.java | 35 +++++------ .../java/com/google/cloud/storage/Utils.java | 59 +++---------------- 3 files changed, 24 insertions(+), 71 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java index aa78fa7276..03e9630374 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java @@ -1284,7 +1284,6 @@ public Builder setContexts(ObjectContexts contexts) { NamedField.nested(BlobField.OBJECT_CONTEXTS, NamedField.literal("custom")), left, right, - f -> NamedField.nested(f, NAMED_FIELD_LITERAL_VALUE), modifiedFields::add); this.contexts = contexts; } else { diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java index 75ad5846f7..f5297ee837 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java @@ -22,9 +22,6 @@ import com.google.cloud.storage.UnifiedOpts.NamedField; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.MapDifference; -import com.google.common.collect.MapDifference.ValueDifference; -import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; @@ -41,7 +38,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.NonNull; final class JsonUtils { @@ -104,24 +100,25 @@ static T getOutputJsonWithSelectedFields( static @NonNull JsonObject getOutputJson(JsonObject inputJson, Set fieldsInOutput) { Map l = flatten(inputJson); - Map r = Utils.setToMap(fieldsInOutput, k -> null); - - MapDifference diff = Maps.difference(l, r); // use hashmap so we can have null values HashMap flat = new HashMap<>(); - Stream.of( - diff.entriesInCommon().entrySet().stream(), - diff.entriesOnlyOnRight().entrySet().stream(), - // if the key is present in both maps, but has a differing value select the value from - // the left side, as that is the value from inputJson - Maps.transformValues(diff.entriesDiffering(), ValueDifference::leftValue) - .entrySet() - .stream()) - // flatten - .flatMap(x -> x) - .forEach(e -> flat.put(e.getKey(), e.getValue())); - + for (String fieldToRetain : fieldsInOutput) { + boolean keyFound = false; + // Check for exact match or prefix match in the flattened source map (l) + for (Map.Entry sourceEntry : l.entrySet()) { + String sourceKey = sourceEntry.getKey(); + if (sourceKey.equals(fieldToRetain) || sourceKey.startsWith(fieldToRetain + ".")) { + flat.put(sourceKey, sourceEntry.getValue()); + keyFound = true; + } + } + // If the field to retain wasn't found in the source, it means we need to add it + // to the output with a null value, signaling a deletion. + if (!keyFound) { + flat.put(fieldToRetain, null); + } + } return treeify(flat); } diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java index 5a3217b47b..eece2fe79d 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Utils.java @@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapDifference; -import com.google.common.collect.MapDifference.ValueDifference; import com.google.common.collect.Maps; import com.google.common.io.BaseEncoding; import com.google.common.primitives.Ints; @@ -282,70 +281,28 @@ static T firstNonNull(Supplier<@Nullable T>... ss) { */ static void diffMaps( NamedField parent, Map left, Map right, Consumer sink) { - diffMaps(parent, left, right, Function.identity(), sink); - } - - /** - * Diff two maps, and append each differing key to {@code sink} with the parent of {{@code - * parent}. Conditionally apply {@code dec} if deeper qualification is necessary. - */ - static void diffMaps( - NamedField parent, - Map left, - Map right, - Function dec, - Consumer sink) { - final Stream keys; + final Stream keys; if (left != null && right == null) { - keys = left.keySet().stream().map(NamedField::literal); + keys = left.keySet().stream(); } else if (left == null && right != null) { - keys = - right.entrySet().stream() - .map( - e -> { - String key = e.getKey(); - Object value = e.getValue(); - NamedField literal = NamedField.literal(key); - - if (value == null) { - return literal; - } else { - return dec.apply(literal); - } - }); + keys = right.keySet().stream(); } else if (left != null && right != null) { MapDifference difference = Maps.difference(left, right); keys = Stream.of( // keys with modified values - difference.entriesDiffering().entrySet().stream() - .map( - e -> { - String key = e.getKey(); - NamedField literal = NamedField.literal(key); - ValueDifference diff = e.getValue(); - - if (diff.leftValue() != null && diff.rightValue() == null) { - return literal; - } else if (diff.leftValue() == null && diff.rightValue() != null) { - return literal; - } else { - return dec.apply(literal); - } - }), + difference.entriesDiffering().keySet().stream(), // Only include keys to remove if ALL keys were removed right.isEmpty() - ? difference.entriesOnlyOnLeft().keySet().stream().map(NamedField::literal) - : Stream.empty(), + ? difference.entriesOnlyOnLeft().keySet().stream() + : Stream.empty(), // new keys - difference.entriesOnlyOnRight().keySet().stream() - .map(NamedField::literal) - .map(dec)) + difference.entriesOnlyOnRight().keySet().stream()) .flatMap(x -> x); } else { keys = Stream.empty(); } - keys.map(k -> NamedField.nested(parent, k)).forEach(sink); + keys.map(NamedField::literal).map(k -> NamedField.nested(parent, k)).forEach(sink); } static T[] subArray(T[] ts, int offset, int length) { From 52a8dd2977fee008ced135496f4411e896dfd6b8 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Mon, 15 Sep 2025 21:23:39 +0530 Subject: [PATCH 3/6] chore: remove ignoreCustomContextPayloadTimestamps --- .../com/google/cloud/storage/BlobInfo.java | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java index 03e9630374..a083338e74 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java @@ -33,7 +33,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; import com.google.common.io.BaseEncoding; import java.io.Serializable; import java.nio.ByteBuffer; @@ -1273,11 +1272,9 @@ public Builder setContexts(ObjectContexts contexts) { // about the timestamps when determining if a value needs to be patched. Create a new map // where we remove the timestamps so equals is usable. Map left = - this.contexts == null - ? null - : ignoreCustomContextPayloadTimestamps(this.contexts.getCustom()); + this.contexts == null ? null : this.contexts.getCustom(); Map right = - contexts == null ? null : ignoreCustomContextPayloadTimestamps(contexts.getCustom()); + contexts == null ? null : contexts.getCustom(); if (!Objects.equals(left, right)) { if (right != null) { diffMaps( @@ -1294,20 +1291,6 @@ public Builder setContexts(ObjectContexts contexts) { return this; } - private static @Nullable Map<@NonNull String, @Nullable ObjectCustomContextPayload> - ignoreCustomContextPayloadTimestamps( - @Nullable Map<@NonNull String, @Nullable ObjectCustomContextPayload> orig) { - if (orig == null) { - return null; - } - return Maps.transformValues( - orig, - v -> - v == null - ? null - : ObjectCustomContextPayload.newBuilder().setValue(v.getValue()).build()); - } - @Override public BlobInfo build() { checkNotNull(blobId); From d6f494407bc64e3918a0ea2fbc10626c8b45b960 Mon Sep 17 00:00:00 2001 From: nidhiii-27 Date: Tue, 16 Sep 2025 14:17:22 +0530 Subject: [PATCH 4/6] chore: add unit tests for diffing logic --- .../google/cloud/storage/BlobInfoTest.java | 51 +++++++++++++++++++ .../com/google/cloud/storage/TestUtils.java | 11 ++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java index 862709a5ae..6ecb483918 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java @@ -19,6 +19,8 @@ import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS; import static com.google.cloud.storage.Acl.Role.READER; import static com.google.cloud.storage.Acl.Role.WRITER; +import static com.google.cloud.storage.TestUtils.hashMapOf; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -30,12 +32,16 @@ import com.google.cloud.storage.BlobInfo.CustomerEncryption; import com.google.cloud.storage.BlobInfo.ObjectContexts; import com.google.cloud.storage.BlobInfo.ObjectCustomContextPayload; +import com.google.cloud.storage.UnifiedOpts.NamedField; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import java.math.BigInteger; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.junit.Test; public class BlobInfoTest { @@ -352,4 +358,49 @@ public void testToPbAndFromPb() { public void testBlobId() { assertEquals(BlobId.of("b", "n", GENERATION), BLOB_INFO.getBlobId()); } + + @Test + public void deepFieldDiffDetectionWorksCorrectly_mutateRetrievedObject() { + BlobInfo info = + BlobInfo.newBuilder("bucket", "object") + .setContexts( + ObjectContexts.newBuilder() + .setCustom( + hashMapOf( + "c1", ObjectCustomContextPayload.newBuilder().setValue("C1").build(), + "c2", ObjectCustomContextPayload.newBuilder().setValue("C2").build())) + .build()) + .setMetadata( + hashMapOf( + "m1", "M1", + "m2", "M2")) + .build(); + + BlobInfo modified = + info.toBuilder() + .setMetadata(hashMapOf("m2", null)) + .setContexts(ObjectContexts.newBuilder().setCustom(hashMapOf("k2", null)).build()) + .build(); + Set modifiedFields = + modified.getModifiedFields().stream() + .map(NamedField::getGrpcName) + .collect(Collectors.toSet()); + + assertThat(modifiedFields).isEqualTo(ImmutableSet.of("contexts.custom.k2", "metadata.m2")); + } + + @Test + public void deepFieldDiffDetectionWorksCorrectly_declaredDiff() { + BlobInfo modified = + BlobInfo.newBuilder("bucket", "object") + .setMetadata(hashMapOf("m2", null)) + .setContexts(ObjectContexts.newBuilder().setCustom(hashMapOf("k2", null)).build()) + .build(); + Set modifiedFields = + modified.getModifiedFields().stream() + .map(UnifiedOpts.NamedField::getGrpcName) + .collect(Collectors.toSet()); + + assertThat(modifiedFields).isEqualTo(ImmutableSet.of("contexts.custom.k2", "metadata.m2")); + } } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java index f493570acc..5231b8fe00 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java @@ -279,20 +279,19 @@ public static void assertAll(ThrowingRunnable... trs) throws Exception { } /** ImmutableMap does not allow null values, this method does */ - public static Map<@NonNull String, @Nullable String> hashMapOf( - @NonNull String k1, @Nullable String v1) { + public static Map<@NonNull K, @Nullable V> hashMapOf(@NonNull K k1, @Nullable V v1) { requireNonNull(k1, "k1 must be non null"); - HashMap map = new HashMap<>(); + HashMap map = new HashMap<>(); map.put(k1, v1); return Collections.unmodifiableMap(map); } /** ImmutableMap does not allow null values, this method does */ - public static Map<@NonNull String, @Nullable String> hashMapOf( - @NonNull String k1, @Nullable String v1, @NonNull String k2, @Nullable String v2) { + public static Map<@NonNull K, @Nullable V> hashMapOf( + @NonNull K k1, @Nullable V v1, @NonNull K k2, @Nullable V v2) { requireNonNull(k1, "k1 must be non null"); requireNonNull(k2, "k2 must be non null"); - HashMap map = new HashMap<>(); + HashMap map = new HashMap<>(); map.put(k1, v1); map.put(k2, v2); return Collections.unmodifiableMap(map); From 9eb0acf7248d175ffbe02803acf2aed396d0d079 Mon Sep 17 00:00:00 2001 From: nidhiii-27 Date: Fri, 19 Sep 2025 12:32:17 +0530 Subject: [PATCH 5/6] chore: add tests for updating base on new BlobInfo --- .../storage/it/ITNestedUpdateMaskTest.java | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java index e8e3a25728..27c16fbc8e 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java @@ -20,15 +20,10 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.Objects.requireNonNull; -import com.google.cloud.storage.Blob; -import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.*; import com.google.cloud.storage.BlobInfo.ObjectContexts; import com.google.cloud.storage.BlobInfo.ObjectCustomContextPayload; -import com.google.cloud.storage.Bucket; -import com.google.cloud.storage.BucketInfo; -import com.google.cloud.storage.Storage; import com.google.cloud.storage.Storage.BlobTargetOption; -import com.google.cloud.storage.Storage.BucketTargetOption; import com.google.cloud.storage.TransportCompatibility.Transport; import com.google.cloud.storage.it.ITNestedUpdateMaskTest.NestedUpdateMaskParametersProvider; import com.google.cloud.storage.it.runner.StorageITRunner; @@ -78,6 +73,7 @@ public static final class NestedUpdateMaskParametersProvider implements Paramete private static final Map k1a_k2null = hashMapOf("k1", "a", "k2", null); private static final Map k1null = hashMapOf("k1", null); private static final Map k2null = hashMapOf("k2", null); + private static final Map k1null_k2null = hashMapOf("k1", null, "k2", null); /** * @@ -109,9 +105,9 @@ public ImmutableList parameters() { new Param("2 keys, modify 1 value (fine)", k1a_k2b, k1z, k1z_k2b), new Param("2 keys, modify 1 null (full)", k1a_k2b, k1a_k2null, k1a), new Param("2 keys, modify 1 null (fine)", k1a_k2b, k2null, k1a), - new Param("1 key, set empty", k1a, empty, null), + new Param("1 key, set null", k1a, k1null, null), new Param("1 key, null key", k1a, k1null, null), - new Param("2 keys, set null", k1a_k2b, null, null)); + new Param("2 keys, set null", k1a_k2b, k1null_k2null, null)); } } @@ -122,7 +118,7 @@ public void testBucketLabels() throws Exception { TemporaryBucket.newBuilder().setBucketInfo(bucket).setStorage(storage).build()) { BucketInfo gen1 = tempB.getBucket(); BucketInfo modified = gen1.toBuilder().setLabels(param.update).build(); - Bucket gen2 = storage.update(modified, BucketTargetOption.metagenerationMatch()); + Bucket gen2 = storage.update(modified, Storage.BucketTargetOption.metagenerationMatch()); assertThat(gen2.getLabels()).isEqualTo(param.expected); } } @@ -136,6 +132,17 @@ public void testBlobMetadata() { assertThat(gen2.getMetadata()).isEqualTo(param.expected); } + @Test + public void testBlobMetadata_updateBaseOnNewInfoInsteadOfResolvedInfo() { + BlobInfo blob = newBlobInfo(param.initial); + Blob gen1 = storage.create(blob, BlobTargetOption.doesNotExist()); + BlobInfo updated = + BlobInfo.newBuilder(bucket, gen1.getName()).setMetadata(param.update).build(); + Blob gen2 = + storage.update(updated, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); + assertThat(gen2.getMetadata()).isEqualTo(param.expected); + } + @Test public void testBlobContexts() { ObjectContexts initial = contextsFromMap(param.initial); @@ -155,6 +162,26 @@ public void testBlobContexts() { assertContextsWithEqualValues(gen2.getContexts(), expected); } + @Test + public void testBlobContexts_updateBaseOnNewInfoInsteadOfResolvedInfo() { + ObjectContexts initial = contextsFromMap(param.initial); + ObjectContexts update = contextsFromMap(param.update); + ObjectContexts expected = contextsFromMap(param.expected); + + String blobName = generator.randomObjectName(); + BlobInfo.Builder builder = BlobInfo.newBuilder(bucket, blobName); + if (initial != null) { + builder.setContexts(initial); + } + BlobInfo info = builder.build(); + Blob gen1 = storage.create(info, BlobTargetOption.doesNotExist()); + + BlobInfo modified = BlobInfo.newBuilder(bucket, gen1.getName()).setContexts(update).build(); + Blob gen2 = + storage.update(modified, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); + assertContextsWithEqualValues(gen2.getContexts(), expected); + } + @Test public void testBlob_metadataAndContext() { ObjectContexts initial = contextsFromMap(param.initial); @@ -179,6 +206,35 @@ public void testBlob_metadataAndContext() { assertThat(gen2.getMetadata()).isEqualTo(param.expected); } + @Test + public void testBlob_metadataAndContext_updateBaseOnNewInfoInsteadOfResolvedInfo() { + ObjectContexts initial = contextsFromMap(param.initial); + ObjectContexts update = contextsFromMap(param.update); + ObjectContexts expected = contextsFromMap(param.expected); + + String blobName = generator.randomObjectName(); + BlobInfo.Builder builder = BlobInfo.newBuilder(bucket, blobName); + if (initial != null) { + builder.setContexts(initial); + } + if (param.initial != null) { + builder.setMetadata(param.initial); + } + + BlobInfo info = builder.build(); + Blob gen1 = storage.create(info, BlobTargetOption.doesNotExist()); + + BlobInfo modified = + BlobInfo.newBuilder(bucket, gen1.getName()) + .setContexts(update) + .setMetadata(param.update) + .build(); + Blob gen2 = + storage.update(modified, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); + assertContextsWithEqualValues(gen2.getContexts(), expected); + assertThat(gen2.getMetadata()).isEqualTo(param.expected); + } + private static void assertContextsWithEqualValues( @Nullable ObjectContexts actual, @Nullable ObjectContexts expected) { if (expected != null && !expected.getCustom().isEmpty() && actual != null) { From 64f6ace58ec0ec863417a5b5baea0274abdc399f Mon Sep 17 00:00:00 2001 From: nidhiii-27 Date: Tue, 23 Sep 2025 22:27:00 +0530 Subject: [PATCH 6/6] chore: remove tests for updating based on new BlobInfo temporarily --- .../storage/it/ITNestedUpdateMaskTest.java | 73 +++---------------- 1 file changed, 9 insertions(+), 64 deletions(-) diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java index 27c16fbc8e..e84ccf5a5e 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNestedUpdateMaskTest.java @@ -20,10 +20,15 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.Objects.requireNonNull; -import com.google.cloud.storage.*; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.BlobInfo.ObjectContexts; import com.google.cloud.storage.BlobInfo.ObjectCustomContextPayload; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; import com.google.cloud.storage.Storage.BlobTargetOption; +import com.google.cloud.storage.Storage.BucketTargetOption; import com.google.cloud.storage.TransportCompatibility.Transport; import com.google.cloud.storage.it.ITNestedUpdateMaskTest.NestedUpdateMaskParametersProvider; import com.google.cloud.storage.it.runner.StorageITRunner; @@ -91,7 +96,7 @@ public static final class NestedUpdateMaskParametersProvider implements Paramete * | {"k1":"a","k2":"b"} | {"k2":null} | {"k1":"a"} | * | {"k1":"a"} | {} | null | * | {"k1":"a"} | {"k1":null} | null | - * | {"k1":"a","k2":"b"} | null | null | + * | {"k1":"a","k2":"b"} | {"k1:null,"k2":null} | null | * */ @Override @@ -105,7 +110,7 @@ public ImmutableList parameters() { new Param("2 keys, modify 1 value (fine)", k1a_k2b, k1z, k1z_k2b), new Param("2 keys, modify 1 null (full)", k1a_k2b, k1a_k2null, k1a), new Param("2 keys, modify 1 null (fine)", k1a_k2b, k2null, k1a), - new Param("1 key, set null", k1a, k1null, null), + new Param("1 key, set empty", k1a, empty, null), new Param("1 key, null key", k1a, k1null, null), new Param("2 keys, set null", k1a_k2b, k1null_k2null, null)); } @@ -118,7 +123,7 @@ public void testBucketLabels() throws Exception { TemporaryBucket.newBuilder().setBucketInfo(bucket).setStorage(storage).build()) { BucketInfo gen1 = tempB.getBucket(); BucketInfo modified = gen1.toBuilder().setLabels(param.update).build(); - Bucket gen2 = storage.update(modified, Storage.BucketTargetOption.metagenerationMatch()); + Bucket gen2 = storage.update(modified, BucketTargetOption.metagenerationMatch()); assertThat(gen2.getLabels()).isEqualTo(param.expected); } } @@ -132,17 +137,6 @@ public void testBlobMetadata() { assertThat(gen2.getMetadata()).isEqualTo(param.expected); } - @Test - public void testBlobMetadata_updateBaseOnNewInfoInsteadOfResolvedInfo() { - BlobInfo blob = newBlobInfo(param.initial); - Blob gen1 = storage.create(blob, BlobTargetOption.doesNotExist()); - BlobInfo updated = - BlobInfo.newBuilder(bucket, gen1.getName()).setMetadata(param.update).build(); - Blob gen2 = - storage.update(updated, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); - assertThat(gen2.getMetadata()).isEqualTo(param.expected); - } - @Test public void testBlobContexts() { ObjectContexts initial = contextsFromMap(param.initial); @@ -162,26 +156,6 @@ public void testBlobContexts() { assertContextsWithEqualValues(gen2.getContexts(), expected); } - @Test - public void testBlobContexts_updateBaseOnNewInfoInsteadOfResolvedInfo() { - ObjectContexts initial = contextsFromMap(param.initial); - ObjectContexts update = contextsFromMap(param.update); - ObjectContexts expected = contextsFromMap(param.expected); - - String blobName = generator.randomObjectName(); - BlobInfo.Builder builder = BlobInfo.newBuilder(bucket, blobName); - if (initial != null) { - builder.setContexts(initial); - } - BlobInfo info = builder.build(); - Blob gen1 = storage.create(info, BlobTargetOption.doesNotExist()); - - BlobInfo modified = BlobInfo.newBuilder(bucket, gen1.getName()).setContexts(update).build(); - Blob gen2 = - storage.update(modified, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); - assertContextsWithEqualValues(gen2.getContexts(), expected); - } - @Test public void testBlob_metadataAndContext() { ObjectContexts initial = contextsFromMap(param.initial); @@ -206,35 +180,6 @@ public void testBlob_metadataAndContext() { assertThat(gen2.getMetadata()).isEqualTo(param.expected); } - @Test - public void testBlob_metadataAndContext_updateBaseOnNewInfoInsteadOfResolvedInfo() { - ObjectContexts initial = contextsFromMap(param.initial); - ObjectContexts update = contextsFromMap(param.update); - ObjectContexts expected = contextsFromMap(param.expected); - - String blobName = generator.randomObjectName(); - BlobInfo.Builder builder = BlobInfo.newBuilder(bucket, blobName); - if (initial != null) { - builder.setContexts(initial); - } - if (param.initial != null) { - builder.setMetadata(param.initial); - } - - BlobInfo info = builder.build(); - Blob gen1 = storage.create(info, BlobTargetOption.doesNotExist()); - - BlobInfo modified = - BlobInfo.newBuilder(bucket, gen1.getName()) - .setContexts(update) - .setMetadata(param.update) - .build(); - Blob gen2 = - storage.update(modified, BlobTargetOption.metagenerationMatch(gen1.getMetageneration())); - assertContextsWithEqualValues(gen2.getContexts(), expected); - assertThat(gen2.getMetadata()).isEqualTo(param.expected); - } - private static void assertContextsWithEqualValues( @Nullable ObjectContexts actual, @Nullable ObjectContexts expected) { if (expected != null && !expected.getCustom().isEmpty() && actual != null) {