From 61a915d90303aba71a100e8988dbcbf2eaf42685 Mon Sep 17 00:00:00 2001
From: Jesse Lovelace <jesselovelace@google.com>
Date: Wed, 9 Oct 2024 18:26:38 -0700
Subject: [PATCH 1/3] feat: adds support for restore token

---
 .../java/com/google/cloud/storage/Blob.java   |  6 ++++
 .../com/google/cloud/storage/BlobInfo.java    | 29 ++++++++++++++--
 .../google/cloud/storage/GrpcConversions.java |  6 ++++
 .../google/cloud/storage/JsonConversions.java |  2 ++
 .../com/google/cloud/storage/Storage.java     | 22 +++++++++++-
 .../com/google/cloud/storage/UnifiedOpts.java | 25 ++++++++++++++
 .../cloud/storage/spi/v1/StorageRpc.java      |  1 +
 .../google/cloud/storage/it/ITObjectTest.java | 34 +++++++++++++++++++
 8 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
index c6ea7a2cea..dfe8bcc06f 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
@@ -538,6 +538,12 @@ Builder setHardDeleteTime(OffsetDateTime hardDeleteTime) {
       return this;
     }
 
+    @Override
+    Builder setRestoreToken(String restoreToken){
+      infoBuilder.setRestoreToken(restoreToken);
+      return this;
+    }
+
     @Override
     public Builder setRetention(Retention retention) {
       infoBuilder.setRetention(retention);
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 52ce09fd7e..e034e9d7b2 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
@@ -110,6 +110,7 @@ public class BlobInfo implements Serializable {
   private final Retention retention;
   private final OffsetDateTime softDeleteTime;
   private final OffsetDateTime hardDeleteTime;
+  private final String restoreToken;
   private final transient ImmutableSet<NamedField> modifiedFields;
 
   /** This class is meant for internal use only. Users are discouraged from using this class. */
@@ -531,6 +532,8 @@ Builder setRetentionExpirationTimeOffsetDateTime(OffsetDateTime retentionExpirat
 
     abstract Builder setHardDeleteTime(OffsetDateTime hardDeleteTIme);
 
+    abstract Builder setRestoreToken(String restoreToken);
+
     public abstract Builder setRetention(Retention retention);
 
     /** Creates a {@code BlobInfo} object. */
@@ -634,6 +637,7 @@ static final class BuilderImpl extends Builder {
     private Retention retention;
     private OffsetDateTime softDeleteTime;
     private OffsetDateTime hardDeleteTime;
+    private String restoreToken;
     private final ImmutableSet.Builder<NamedField> modifiedFields = ImmutableSet.builder();
 
     BuilderImpl(BlobId blobId) {
@@ -674,6 +678,7 @@ static final class BuilderImpl extends Builder {
       retention = blobInfo.retention;
       softDeleteTime = blobInfo.softDeleteTime;
       hardDeleteTime = blobInfo.hardDeleteTime;
+      restoreToken = blobInfo.restoreToken;
     }
 
     @Override
@@ -1065,6 +1070,15 @@ Builder setHardDeleteTime(OffsetDateTime hardDeleteTime) {
       return this;
     }
 
+    @Override
+    Builder setRestoreToken(String restoreToken) {
+      if(!Objects.equals(this.restoreToken, restoreToken)){
+        modifiedFields.add(BlobField.RESTORE_TOKEN);
+      }
+      this.restoreToken = restoreToken;
+      return this;
+    }
+
     @Override
     public Builder setRetention(Retention retention) {
       // todo: b/308194853
@@ -1299,6 +1313,7 @@ Builder clearRetentionExpirationTime() {
     retention = builder.retention;
     softDeleteTime = builder.softDeleteTime;
     hardDeleteTime = builder.hardDeleteTime;
+    restoreToken = builder.restoreToken;
     modifiedFields = builder.modifiedFields.build();
   }
 
@@ -1704,6 +1719,14 @@ public OffsetDateTime getHardDeleteTime() {
     return hardDeleteTime;
   }
 
+  /**
+   * If this is a soft-deleted object in an HNS-enabled bucket, returns the restore token which will
+   * be necessary to restore it if there's a name conflict with another object.
+   */
+  public String getRestoreToken() {
+    return restoreToken;
+  }
+
   /** Returns the object's Retention policy. */
   public Retention getRetention() {
     return retention;
@@ -1761,7 +1784,8 @@ public int hashCode() {
         retention,
         retentionExpirationTime,
         softDeleteTime,
-        hardDeleteTime);
+        hardDeleteTime,
+        restoreToken);
   }
 
   @Override
@@ -1805,7 +1829,8 @@ public boolean equals(Object o) {
         && Objects.equals(retentionExpirationTime, blobInfo.retentionExpirationTime)
         && Objects.equals(retention, blobInfo.retention)
         && Objects.equals(softDeleteTime, blobInfo.softDeleteTime)
-        && Objects.equals(hardDeleteTime, blobInfo.hardDeleteTime);
+        && Objects.equals(hardDeleteTime, blobInfo.hardDeleteTime)
+        && Objects.equals(restoreToken, blobInfo.restoreToken);
   }
 
   ImmutableSet<NamedField> getModifiedFields() {
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
index 7396e0ae69..17d74a1d4d 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
@@ -886,6 +886,8 @@ private Object blobInfoEncode(BlobInfo from) {
     ifNonNull(from.getCustomTimeOffsetDateTime(), timestampCodec::encode, toBuilder::setCustomTime);
     ifNonNull(from.getSoftDeleteTime(), timestampCodec::encode, toBuilder::setSoftDeleteTime);
     ifNonNull(from.getHardDeleteTime(), timestampCodec::encode, toBuilder::setHardDeleteTime);
+    // TODO: uncomment when grpc is available
+    //ifNonNull(from.getRestoreToken(), toBuilder::setRestoreToken);
     ifNonNull(
         from.getCustomerEncryption(),
         customerEncryptionCodec::encode,
@@ -957,6 +959,10 @@ private BlobInfo blobInfoDecode(Object from) {
     if (from.hasHardDeleteTime()) {
       toBuilder.setHardDeleteTime(timestampCodec.decode(from.getHardDeleteTime()));
     }
+    /* TODO: uncomment when grpc is available
+    if (from.hasRestoreToken()) {
+      toBuilder.setRestoreToken(from.getRestoreToken());
+    }*/
     String storageClass = from.getStorageClass();
     if (!storageClass.isEmpty()) {
       toBuilder.setStorageClass(StorageClass.valueOf(storageClass));
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonConversions.java
index 12562cb020..316cd00335 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonConversions.java
@@ -254,6 +254,7 @@ private StorageObject blobInfoEncode(BlobInfo from) {
 
     ifNonNull(from.getSoftDeleteTime(), dateTimeCodec::encode, to::setSoftDeleteTime);
     ifNonNull(from.getHardDeleteTime(), dateTimeCodec::encode, to::setHardDeleteTime);
+    ifNonNull(from.getRestoreToken(), to::setRestoreToken);
 
     // todo: clean this up once retention is enabled in grpc
     // This is a workaround so that explicitly null retention objects are only included when the
@@ -338,6 +339,7 @@ private BlobInfo blobInfoDecode(StorageObject from) {
     ifNonNull(from.getRetention(), this::retentionDecode, to::setRetention);
     ifNonNull(from.getSoftDeleteTime(), dateTimeCodec::decode, to::setSoftDeleteTime);
     ifNonNull(from.getHardDeleteTime(), dateTimeCodec::decode, to::setHardDeleteTime);
+    ifNonNull(from.getRestoreToken(), to::setRestoreToken);
     return to.build();
   }
 
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
index 0729b47225..6e4bd2aea9 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
@@ -326,8 +326,10 @@ enum BlobField implements FieldSelector, NamedField {
 
     @TransportCompatibility({Transport.HTTP, Transport.GRPC})
     HARD_DELETE_TIME(
-        "hardDeleteTime", "hard_delete_time", com.google.api.client.util.DateTime.class);
+        "hardDeleteTime", "hard_delete_time", com.google.api.client.util.DateTime.class),
 
+    @TransportCompatibility({Transport.HTTP, Transport.GRPC})
+    RESTORE_TOKEN("restoreToken", "restore_token", String.class);
     static final List<NamedField> REQUIRED_FIELDS = ImmutableList.of(BUCKET, NAME);
     private static final Map<String, BlobField> JSON_FIELD_NAME_INDEX;
 
@@ -1687,6 +1689,15 @@ public static BlobGetOption softDeleted(boolean softDeleted) {
       return new BlobGetOption(UnifiedOpts.softDeleted(softDeleted));
     }
 
+    /**
+     * Returns an option that must be specified when getting a soft-deleted object from an HNS-enabled
+     * bucket that has a name/generation conflict with another object in the same bucket.
+     */
+    @TransportCompatibility({Transport.HTTP, Transport.GRPC})
+    public static BlobGetOption restoreToken(String restoreToken){
+      return new BlobGetOption(UnifiedOpts.restoreToken(restoreToken));
+    }
+
     /**
      * Deduplicate any options which are the same parameter. The value which comes last in {@code
      * os} will be the value included in the return.
@@ -1775,6 +1786,15 @@ public static BlobRestoreOption metagenerationNotMatch(long generation) {
     public static BlobRestoreOption copySourceAcl(boolean copySourceAcl) {
       return new BlobRestoreOption(UnifiedOpts.copySourceAcl(copySourceAcl));
     }
+
+    /**
+     * Returns an option that must be specified when getting a soft-deleted object from an HNS-enabled
+     * bucket that has a name/generation conflict with another object in the same bucket.
+     */
+    @TransportCompatibility({Transport.HTTP, Transport.GRPC})
+    public static BlobRestoreOption restoreToken(String restoreToken) {
+      return new BlobRestoreOption(UnifiedOpts.restoreToken(restoreToken));
+    }
   }
 
   /** Class for specifying bucket list options. */
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 bcc987ea75..45d87dbf16 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
@@ -502,6 +502,10 @@ static SoftDeleted softDeleted(boolean softDeleted) {
     return new SoftDeleted(softDeleted);
   }
 
+  static RestoreToken restoreToken(String restoreToken){
+    return new RestoreToken(restoreToken);
+  }
+
   static CopySourceAcl copySourceAcl(boolean copySourceAcl) {
     return new CopySourceAcl(copySourceAcl);
   }
@@ -719,6 +723,27 @@ public Mapper<GetObjectRequest.Builder> getObject() {
     }
   }
 
+  static final class RestoreToken extends RpcOptVal<String> implements ObjectSourceOpt {
+
+    private static final long serialVersionUID = 4215757108268532746L;
+
+    private RestoreToken(String val) {
+      super(StorageRpc.Option.RESTORE_TOKEN, val);
+    }
+    
+    /* TODO: uncomment when grpc is available
+    @Override
+    public Mapper<RestoreObjectRequest.Builder> restoreObject() {
+      return b -> b.setRestoreToken(val);
+    }
+
+    @Override
+    public Mapper<GetObjectRequest.Builder> getObject() {
+      return b-> b.setRestoreToken(val);
+    }
+    */
+  }
+
   static final class CopySourceAcl extends RpcOptVal<Boolean> implements ObjectSourceOpt {
 
     private static final long serialVersionUID = 2033755749149128119L;
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
index 78747d42d6..b4a2ddfcc6 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
@@ -75,6 +75,7 @@ enum Option {
     RETURN_RAW_INPUT_STREAM("returnRawInputStream"),
     OVERRIDE_UNLOCKED_RETENTION("overrideUnlockedRetention"),
     SOFT_DELETED("softDeleted"),
+    RESTORE_TOKEN("restoreToken"),
     COPY_SOURCE_ACL("copySourceAcl"),
     GENERATION("generation"),
     INCLUDE_FOLDERS_AS_PREFIXES("includeFoldersAsPrefixes"),
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
index 99ed548098..ec838406dd 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
@@ -1525,4 +1525,38 @@ public void testUpdateBlob_noModification() {
     Blob gen2 = storage.update(gen1);
     assertThat(gen2).isEqualTo(gen1);
   }
+
+  @Test
+  @Exclude(transports = Transport.GRPC) // TODO: remove when grpc is available
+  public void testRestoreToken() {
+    String bucketName = generator.randomBucketName();
+    storage.create(
+            BucketInfo.newBuilder(bucketName)
+                    .setHierarchicalNamespace(
+                            BucketInfo.HierarchicalNamespace.newBuilder().setEnabled(true).build())
+                    .setIamConfiguration(
+                            BucketInfo.IamConfiguration.newBuilder()
+                                    .setIsUniformBucketLevelAccessEnabled(true)
+                                    .build())
+                    .build());
+    BlobInfo info = BlobInfo.newBuilder(bucketName, generator.randomObjectName()).build();
+    try {
+      Blob delobj = storage.create(info);
+      storage.delete(delobj.getBlobId());
+
+      Blob got = storage.get(delobj.getBlobId(), BlobGetOption.softDeleted(true));
+      assertThat(got.getRestoreToken()).isNotNull();
+
+      Blob gotWithRestoreToken = storage.get(delobj.getBlobId(), BlobGetOption.softDeleted(true), BlobGetOption.restoreToken(got.getRestoreToken()));
+      assertThat(gotWithRestoreToken).isNotNull();
+
+      storage.restore(got.getBlobId(), Storage.BlobRestoreOption.restoreToken(got.getRestoreToken()));
+      assertThat(storage.get(bucketName, delobj.getName())).isNotNull();;
+
+    } finally {
+      storage.delete(info.getBlobId());
+      storage.delete(bucketName);
+    }
+
+  }
 }

From 39b52f828abd842513b3f949b1a54d7bd03de12b Mon Sep 17 00:00:00 2001
From: Jesse Lovelace <jesselovelace@google.com>
Date: Thu, 31 Oct 2024 11:37:55 -0700
Subject: [PATCH 2/3] add grpc

---
 .../java/com/google/cloud/storage/Blob.java   |  2 +-
 .../com/google/cloud/storage/BlobInfo.java    |  2 +-
 .../google/cloud/storage/GrpcConversions.java |  6 ++--
 .../com/google/cloud/storage/Storage.java     | 12 ++++----
 .../com/google/cloud/storage/UnifiedOpts.java |  8 ++---
 .../google/cloud/storage/it/ITObjectTest.java | 30 +++++++++++--------
 6 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
index 62c70dc569..b0208191a3 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java
@@ -535,7 +535,7 @@ Builder setHardDeleteTime(OffsetDateTime hardDeleteTime) {
     }
 
     @Override
-    Builder setRestoreToken(String restoreToken){
+    Builder setRestoreToken(String restoreToken) {
       infoBuilder.setRestoreToken(restoreToken);
       return this;
     }
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 e034e9d7b2..7494c38b1f 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
@@ -1072,7 +1072,7 @@ Builder setHardDeleteTime(OffsetDateTime hardDeleteTime) {
 
     @Override
     Builder setRestoreToken(String restoreToken) {
-      if(!Objects.equals(this.restoreToken, restoreToken)){
+      if (!Objects.equals(this.restoreToken, restoreToken)) {
         modifiedFields.add(BlobField.RESTORE_TOKEN);
       }
       this.restoreToken = restoreToken;
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
index ae0d9a739c..e87aef5a0d 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
@@ -834,8 +834,7 @@ private Object blobInfoEncode(BlobInfo from) {
     ifNonNull(from.getCustomTimeOffsetDateTime(), timestampCodec::encode, toBuilder::setCustomTime);
     ifNonNull(from.getSoftDeleteTime(), timestampCodec::encode, toBuilder::setSoftDeleteTime);
     ifNonNull(from.getHardDeleteTime(), timestampCodec::encode, toBuilder::setHardDeleteTime);
-    // TODO: uncomment when grpc is available
-    //ifNonNull(from.getRestoreToken(), toBuilder::setRestoreToken);
+    ifNonNull(from.getRestoreToken(), toBuilder::setRestoreToken);
     ifNonNull(
         from.getCustomerEncryption(),
         customerEncryptionCodec::encode,
@@ -907,10 +906,9 @@ private BlobInfo blobInfoDecode(Object from) {
     if (from.hasHardDeleteTime()) {
       toBuilder.setHardDeleteTime(timestampCodec.decode(from.getHardDeleteTime()));
     }
-    /* TODO: uncomment when grpc is available
     if (from.hasRestoreToken()) {
       toBuilder.setRestoreToken(from.getRestoreToken());
-    }*/
+    }
     String storageClass = from.getStorageClass();
     if (!storageClass.isEmpty()) {
       toBuilder.setStorageClass(StorageClass.valueOf(storageClass));
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
index 473c59d4cb..6d3ef93733 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
@@ -1659,11 +1659,12 @@ public static BlobGetOption softDeleted(boolean softDeleted) {
     }
 
     /**
-     * Returns an option that must be specified when getting a soft-deleted object from an HNS-enabled
-     * bucket that has a name/generation conflict with another object in the same bucket.
+     * Returns an option that must be specified when getting a soft-deleted object from an
+     * HNS-enabled bucket that has a name/generation conflict with another object in the same
+     * bucket.
      */
     @TransportCompatibility({Transport.HTTP, Transport.GRPC})
-    public static BlobGetOption restoreToken(String restoreToken){
+    public static BlobGetOption restoreToken(String restoreToken) {
       return new BlobGetOption(UnifiedOpts.restoreToken(restoreToken));
     }
 
@@ -1754,8 +1755,9 @@ public static BlobRestoreOption copySourceAcl(boolean copySourceAcl) {
     }
 
     /**
-     * Returns an option that must be specified when getting a soft-deleted object from an HNS-enabled
-     * bucket that has a name/generation conflict with another object in the same bucket.
+     * Returns an option that must be specified when getting a soft-deleted object from an
+     * HNS-enabled bucket that has a name/generation conflict with another object in the same
+     * bucket.
      */
     @TransportCompatibility({Transport.HTTP, Transport.GRPC})
     public static BlobRestoreOption restoreToken(String restoreToken) {
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 b7e4dec145..acfb00f94a 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
@@ -477,7 +477,7 @@ static SoftDeleted softDeleted(boolean softDeleted) {
     return new SoftDeleted(softDeleted);
   }
 
-  static RestoreToken restoreToken(String restoreToken){
+  static RestoreToken restoreToken(String restoreToken) {
     return new RestoreToken(restoreToken);
   }
 
@@ -705,8 +705,7 @@ static final class RestoreToken extends RpcOptVal<String> implements ObjectSourc
     private RestoreToken(String val) {
       super(StorageRpc.Option.RESTORE_TOKEN, val);
     }
-    
-    /* TODO: uncomment when grpc is available
+
     @Override
     public Mapper<RestoreObjectRequest.Builder> restoreObject() {
       return b -> b.setRestoreToken(val);
@@ -714,9 +713,8 @@ public Mapper<RestoreObjectRequest.Builder> restoreObject() {
 
     @Override
     public Mapper<GetObjectRequest.Builder> getObject() {
-      return b-> b.setRestoreToken(val);
+      return b -> b.setRestoreToken(val);
     }
-    */
   }
 
   static final class CopySourceAcl extends RpcOptVal<Boolean> implements ObjectSourceOpt {
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
index ac79b5eb1e..036e2a04e8 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
@@ -1526,18 +1526,17 @@ public void testUpdateBlob_noModification() {
   }
 
   @Test
-  @Exclude(transports = Transport.GRPC) // TODO: remove when grpc is available
   public void testRestoreToken() {
     String bucketName = generator.randomBucketName();
     storage.create(
-            BucketInfo.newBuilder(bucketName)
-                    .setHierarchicalNamespace(
-                            BucketInfo.HierarchicalNamespace.newBuilder().setEnabled(true).build())
-                    .setIamConfiguration(
-                            BucketInfo.IamConfiguration.newBuilder()
-                                    .setIsUniformBucketLevelAccessEnabled(true)
-                                    .build())
-                    .build());
+        BucketInfo.newBuilder(bucketName)
+            .setHierarchicalNamespace(
+                BucketInfo.HierarchicalNamespace.newBuilder().setEnabled(true).build())
+            .setIamConfiguration(
+                BucketInfo.IamConfiguration.newBuilder()
+                    .setIsUniformBucketLevelAccessEnabled(true)
+                    .build())
+            .build());
     BlobInfo info = BlobInfo.newBuilder(bucketName, generator.randomObjectName()).build();
     try {
       Blob delobj = storage.create(info);
@@ -1546,16 +1545,21 @@ public void testRestoreToken() {
       Blob got = storage.get(delobj.getBlobId(), BlobGetOption.softDeleted(true));
       assertThat(got.getRestoreToken()).isNotNull();
 
-      Blob gotWithRestoreToken = storage.get(delobj.getBlobId(), BlobGetOption.softDeleted(true), BlobGetOption.restoreToken(got.getRestoreToken()));
+      Blob gotWithRestoreToken =
+          storage.get(
+              delobj.getBlobId(),
+              BlobGetOption.softDeleted(true),
+              BlobGetOption.restoreToken(got.getRestoreToken()));
       assertThat(gotWithRestoreToken).isNotNull();
 
-      storage.restore(got.getBlobId(), Storage.BlobRestoreOption.restoreToken(got.getRestoreToken()));
-      assertThat(storage.get(bucketName, delobj.getName())).isNotNull();;
+      storage.restore(
+          got.getBlobId(), Storage.BlobRestoreOption.restoreToken(got.getRestoreToken()));
+      assertThat(storage.get(bucketName, delobj.getName())).isNotNull();
+      ;
 
     } finally {
       storage.delete(info.getBlobId());
       storage.delete(bucketName);
     }
-
   }
 }

From b885b5f6a823fef2c14836396c14b553630a7a59 Mon Sep 17 00:00:00 2001
From: Jesse Lovelace <jesselovelace@google.com>
Date: Thu, 31 Oct 2024 13:04:45 -0700
Subject: [PATCH 3/3] IT fixes

---
 .../com/google/cloud/storage/it/ITBlobReadMaskTest.java     | 3 ++-
 .../com/google/cloud/storage/it/ITOptionRegressionTest.java | 6 ++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadMaskTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadMaskTest.java
index bf8c48258e..f73074cb94 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadMaskTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadMaskTest.java
@@ -203,7 +203,8 @@ public ImmutableList<?> parameters() {
                   BlobField.RETENTION,
                   LazyAssertion.skip("TODO: jesse fill in buganizer bug here")),
               new Args<>(BlobField.SOFT_DELETE_TIME, LazyAssertion.equal()),
-              new Args<>(BlobField.HARD_DELETE_TIME, LazyAssertion.equal()));
+              new Args<>(BlobField.HARD_DELETE_TIME, LazyAssertion.equal()),
+              new Args<>(BlobField.RESTORE_TOKEN, LazyAssertion.equal()));
       List<String> argsDefined =
           args.stream().map(Args::getField).map(Enum::name).sorted().collect(Collectors.toList());
 
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITOptionRegressionTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITOptionRegressionTest.java
index c7032ea618..7041f2df50 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITOptionRegressionTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITOptionRegressionTest.java
@@ -743,7 +743,8 @@ public void storage_BlobGetOption_fields_BlobField() {
             "updated",
             "retention",
             "softDeleteTime",
-            "hardDeleteTime");
+            "hardDeleteTime",
+            "restoreToken");
     s.get(o.getBlobId(), BlobGetOption.fields(BlobField.values()));
     requestAuditing.assertQueryParam("fields", expected, splitOnCommaToSet());
   }
@@ -923,7 +924,8 @@ public void storage_BlobListOption_fields_BlobField() {
             "items/updated",
             "items/retention",
             "items/softDeleteTime",
-            "items/hardDeleteTime");
+            "items/hardDeleteTime",
+            "items/restoreToken");
     s.list(b.getName(), BlobListOption.fields(BlobField.values()));
     requestAuditing.assertQueryParam("fields", expected, splitOnCommaToSet());
   }