Repository: googleapis/java-storage
Pull request #888 (2021-06-18 00:37:36)

feat: Add from and to storage url options for BlobId
Adds options for creating a blob ID from a storage url and vice versa. Fixes https://github.com/googleapis/java-storage/issues/868


Patch:
diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata
index 8cb3de9c..411acf73 100644
--- a/.github/readme/synth.metadata/synth.metadata
+++ b/.github/readme/synth.metadata/synth.metadata
@@ -4,14 +4,14 @@
       "git": {
         "name": ".",
         "remote": "https://github.com/googleapis/java-storage.git",
-        "sha": "76e0cff592329c817db94e96d817070575bf959f"
+        "sha": "45a2fff27cfe80b83fbfa24c6a490be502348db8"
       }
     },
     {
       "git": {
         "name": "synthtool",
         "remote": "https://github.com/googleapis/synthtool.git",
-        "sha": "8f76a885deaaf2fe234daeba4a8cc4d1b3de8086"
+        "sha": "9763f20e4b7bb1091082462b2f7970e965d0d414"
       }
     }
   ]
diff --git a/.kokoro/coerce_logs.sh b/.kokoro/coerce_logs.sh
index 5cf7ba49..46edbf7f 100755
--- a/.kokoro/coerce_logs.sh
+++ b/.kokoro/coerce_logs.sh
@@ -28,7 +28,6 @@ job=$(basename ${KOKORO_JOB_NAME})
 echo "coercing sponge logs..."
 for xml in `find . -name *-sponge_log.xml`
 do
-  echo "processing ${xml}"
   class=$(basename ${xml} | cut -d- -f2)
   dir=$(dirname ${xml})/${job}/${class}
   text=$(dirname ${xml})/${class}-sponge_log.txt
diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh
index a7e999d3..9030ba8f 100755
--- a/.kokoro/dependencies.sh
+++ b/.kokoro/dependencies.sh
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 set -eo pipefail
+shopt -s nullglob
 
 ## Get the directory of the build script
 scriptDir=$(realpath $(dirname "${BASH_SOURCE[0]}"))
diff --git a/README.md b/README.md
index c615a3b8..f39fc8f9 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>libraries-bom</artifactId>
-      <version>20.6.0</version>
+      <version>20.7.0</version>
       <type>pom</type>
       <scope>import</scope>
     </dependency>
@@ -45,7 +45,7 @@ If you are using Maven without BOM, add this to your dependencies:
 
 If you are using Gradle 5.x or later, add this to your dependencies
 ```Groovy
-implementation platform('com.google.cloud:libraries-bom:20.6.0')
+implementation platform('com.google.cloud:libraries-bom:20.7.0')
 
 compile 'com.google.cloud:google-cloud-storage'
 ```
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobId.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobId.java
index 6a0953f9..599f45e2 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobId.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobId.java
@@ -22,6 +22,7 @@ import com.google.api.services.storage.model.StorageObject;
 import com.google.common.base.MoreObjects;
 import java.io.Serializable;
 import java.util.Objects;
+import java.util.regex.Pattern;
 
 /**
  * Google Storage Object identifier. A {@code BlobId} object includes the name of the containing
@@ -56,6 +57,11 @@ public final class BlobId implements Serializable {
     return generation;
   }
 
+  /** Returns this blob's Storage url which can be used with gsutil */
+  public String toGsUtilUri() {
+    return "gs://" + bucket + "/" + name;
+  }
+
   @Override
   public String toString() {
     return MoreObjects.toStringHelper(this)
@@ -114,6 +120,23 @@ public final class BlobId implements Serializable {
     return new BlobId(checkNotNull(bucket), checkNotNull(name), generation);
   }
 
+  /**
+   * Creates a {@code BlobId} object.
+   *
+   * @param gsUtilUri the Storage url to create the blob from
+   */
+  public static BlobId fromGsUtilUri(String gsUtilUri) {
+    if (!Pattern.matches("gs://.*/.*", gsUtilUri)) {
+      throw new IllegalArgumentException(
+          gsUtilUri + " is not a valid gsutil URI (i.e. \"gs://bucket/blob\")");
+    }
+    int blobNameStartIndex = gsUtilUri.indexOf('/', 5);
+    String bucketName = gsUtilUri.substring(5, blobNameStartIndex);
+    String blobName = gsUtilUri.substring(blobNameStartIndex + 1);
+
+    return BlobId.of(bucketName, blobName);
+  }
+
   static BlobId fromPb(StorageObject storageObject) {
     return BlobId.of(
         storageObject.getBucket(), storageObject.getName(), storageObject.getGeneration());
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
index c1339523..95c1d78e 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
@@ -341,6 +341,11 @@ public class BucketInfo implements Serializable {
    * Lifecycle rule for a bucket. Allows supported Actions, such as deleting and changing storage
    * class, to be executed when certain Conditions are met.
    *
+   * <p>Versions 1.50.0-1.111.2 of this library don’t support the CustomTimeBefore,
+   * DaysSinceCustomTime, DaysSinceNoncurrentTime and NoncurrentTimeBefore lifecycle conditions. To
+   * read GCS objects with those lifecycle conditions, update your Java client library to version
+   * 1.112.0 or higher.
+   *
    * @see <a href="https://cloud.google.com/storage/docs/lifecycle#actions">Object Lifecycle
    *     Management</a>
    */
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 0183d6d2..21cb1c4a 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>20.6.0</version>
+        <version>20.7.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/synth.metadata b/synth.metadata
index 73fa782d..5ec465c3 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,14 +4,14 @@
       "git": {
         "name": ".",
         "remote": "https://github.com/googleapis/java-storage.git",
-        "sha": "79f93beaa860ee7ce1e3ad7a255d5a413019b60b"
+        "sha": "3c792b47487c4f3f83ebb5ffc0bcfc3f6cf0f297"
       }
     },
     {
       "git": {
         "name": "synthtool",
         "remote": "https://github.com/googleapis/synthtool.git",
-        "sha": "8f76a885deaaf2fe234daeba4a8cc4d1b3de8086"
+        "sha": "87254ac89a9559864c0a245d6b150406439ce3d8"
       }
     }
   ],


Test patch:
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java
index d503fb76..d90760a4 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java
@@ -31,6 +31,14 @@ public class BlobIdTest {
     assertEquals("n", blobId.getName());
   }
 
+  @Test
+  public void testToFromGsUtilUri() {
+    BlobId blobId = BlobId.fromGsUtilUri("gs://bucket/path/to/blob");
+    assertEquals("bucket", blobId.getBucket());
+    assertEquals("path/to/blob", blobId.getName());
+    assertEquals("gs://bucket/path/to/blob", blobId.toGsUtilUri());
+  }
+
   @Test
   public void testEquals() {
     compareBlobIds(BLOB, BlobId.of("b", "n"));