From 02dc3b5e5377d8848c889647e72102cd9acc646d Mon Sep 17 00:00:00 2001 From: Dmitry <58846611+dmitry-fa@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:47:40 +0300 Subject: [PATCH 01/35] fix: PostPolicyV4.PostFieldsV4.Builder.addCustomMetadataField() allows to add prefixed an not prefixed custom fields (#398) * changes to PostPolicyV4 * PostPolicyV4.addCustomMetadataField() allows to add prefixed an not prefixed custom fields * deprecate incorrectly named method * fix format * addCustomMetadataField -> setCustomMetadataField --- .../google/cloud/storage/PostPolicyV4.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java index df2936dc9..cc9b413a2 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java @@ -31,8 +31,8 @@ * @see POST Object */ public final class PostPolicyV4 { - private String url; - private Map fields; + private final String url; + private final Map fields; private PostPolicyV4(String url, Map fields) { this.url = url; @@ -58,7 +58,7 @@ public Map getFields() { * Object Form fields */ public static final class PostFieldsV4 { - private Map fieldsMap; + private final Map fieldsMap; private PostFieldsV4(Builder builder) { this.fieldsMap = builder.fieldsMap; @@ -81,10 +81,11 @@ public Map getFieldsMap() { } public static class Builder { - private Map fieldsMap; + private static final String CUSTOM_FIELD_PREFIX = "x-goog-meta-"; + private final Map fieldsMap; private Builder() { - fieldsMap = new HashMap<>(); + this.fieldsMap = new HashMap<>(); } public PostFieldsV4 build() { @@ -121,7 +122,13 @@ public Builder setContentType(String contentType) { return this; } + /** @deprecated use {@link #setExpires(String)} */ + @Deprecated public Builder Expires(String expires) { + return setExpires(expires); + } + + public Builder setExpires(String expires) { fieldsMap.put("expires", expires); return this; } @@ -136,8 +143,17 @@ public Builder setSuccessActionStatus(int successActionStatus) { return this; } + /** @deprecated use {@link #setCustomMetadataField(String, String)} */ + @Deprecated public Builder AddCustomMetadataField(String field, String value) { - fieldsMap.put("x-goog-meta-" + field, value); + return setCustomMetadataField(field, value); + } + + public Builder setCustomMetadataField(String field, String value) { + if (!field.startsWith(CUSTOM_FIELD_PREFIX)) { + field = CUSTOM_FIELD_PREFIX + value; + } + fieldsMap.put(field, value); return this; } } @@ -270,8 +286,8 @@ Builder addCustomCondition(ConditionV4Type type, String field, String value) { * Policy document */ public static final class PostPolicyV4Document { - private String expiration; - private PostConditionsV4 conditions; + private final String expiration; + private final PostConditionsV4 conditions; private PostPolicyV4Document(String expiration, PostConditionsV4 conditions) { this.expiration = expiration; @@ -363,9 +379,9 @@ public enum ConditionV4Type { * Policy document */ static final class ConditionV4 { - ConditionV4Type type; - String operand1; - String operand2; + final ConditionV4Type type; + final String operand1; + final String operand2; private ConditionV4(ConditionV4Type type, String operand1, String operand2) { this.type = type; From 05cbbdaf10fc06a2dee3a05f5b0437c79029e1dd Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 14 Jul 2020 22:51:15 +0200 Subject: [PATCH 02/35] chore(deps): update dependency com.google.cloud:google-cloud-storage to v1.111.2 (#427) --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index cbe71fe42..4dad481d1 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -29,7 +29,7 @@ com.google.cloud google-cloud-storage - 1.111.1 + 1.111.2 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index cad01e090..f662ee168 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-storage - 1.111.1 + 1.111.2 From 275f452a5993f95a84fb603a5f4b436238b39439 Mon Sep 17 00:00:00 2001 From: Ajit Thakor <49403056+athakor@users.noreply.github.com> Date: Wed, 15 Jul 2020 21:23:47 +0530 Subject: [PATCH 03/35] fix(docs): example of Storage#testIamPermissions (#434) --- .../src/main/java/com/google/cloud/storage/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eb15dd37a..70863b17f 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 @@ -3517,7 +3517,7 @@ HmacKeyMetadata updateHmacKeyState( * String bucketName = "my-unique-bucket"; * List response = * storage.testIamPermissions( - * bucket, + * bucketName, * ImmutableList.of("storage.buckets.get", "storage.buckets.getIamPolicy")); * for (boolean hasPermission : response) { * // Do something with permission test response From fbfa9ecf277794e07d9a3c46d5b5022f54c37afd Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 16 Jul 2020 00:25:44 +0200 Subject: [PATCH 04/35] deps: update dependency com.google.api-client:google-api-client to v1.30.10 (#423) --- google-cloud-storage/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 196a66538..8b0097909 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -33,7 +33,7 @@ com.google.api-client google-api-client - 1.30.9 + 1.30.10 com.google.apis From 6ef57ebc9eeddc90f13ef87274e8ab0b7eb53290 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 16 Jul 2020 00:51:44 +0200 Subject: [PATCH 05/35] deps: update dependency com.google.apis:google-api-services-storage to v1-rev20200611-1.30.10 (#428) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40d8e2968..353a1b62c 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ com.google.apis google-api-services-storage - v1-rev20200611-1.30.9 + v1-rev20200611-1.30.10 org.easymock From 43202f6124623901abc1d14f4cdb20d08e0db6f1 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 15:52:31 -0700 Subject: [PATCH 06/35] chore: release 1.111.3-SNAPSHOT (#432) * updated versions.txt [ci skip] * updated samples/pom.xml [ci skip] * updated samples/snippets/pom.xml [ci skip] * updated samples/install-without-bom/pom.xml [ci skip] * updated samples/snapshot/pom.xml [ci skip] * updated google-cloud-storage/pom.xml [ci skip] * updated pom.xml Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- google-cloud-storage/pom.xml | 4 ++-- pom.xml | 2 +- versions.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 8b0097909..ffacd7054 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-storage - 1.111.2 + 1.111.3-SNAPSHOT jar Google Cloud Storage https://github.com/googleapis/java-storage @@ -12,7 +12,7 @@ com.google.cloud google-cloud-storage-parent - 1.111.2 + 1.111.3-SNAPSHOT google-cloud-storage diff --git a/pom.xml b/pom.xml index 353a1b62c..853bd01e9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-storage-parent pom - 1.111.2 + 1.111.3-SNAPSHOT Storage Parent https://github.com/googleapis/java-storage diff --git a/versions.txt b/versions.txt index 2eee41669..ef36ef925 100644 --- a/versions.txt +++ b/versions.txt @@ -1,4 +1,4 @@ # Format: # module:released-version:current-version -google-cloud-storage:1.111.2:1.111.2 \ No newline at end of file +google-cloud-storage:1.111.2:1.111.3-SNAPSHOT \ No newline at end of file From 66d1eb793383b9e83992824b392cedd28d54609f Mon Sep 17 00:00:00 2001 From: Dmitry <58846611+dmitry-fa@users.noreply.github.com> Date: Thu, 16 Jul 2020 01:54:32 +0300 Subject: [PATCH 07/35] feat: auto content-type on blob creation (#338) * feat: auto content-type on blob creation * feat: auto content-type on blob creation * feat: auto content-type on blob creation * feat: auto content-type on blob creation * feat: auto content-type on blob creation * feat: auto content-type on blob creation * detection content type is made optional --- .../com/google/cloud/storage/Storage.java | 32 +++++++++-- .../cloud/storage/spi/v1/HttpStorageRpc.java | 23 ++++++-- .../cloud/storage/spi/v1/StorageRpc.java | 3 +- .../cloud/storage/it/ITStorageTest.java | 57 +++++++++++++++++++ 4 files changed, 104 insertions(+), 11 deletions(-) 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 70863b17f..202615cdd 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 @@ -521,6 +521,15 @@ public static BlobTargetOption disableGzipContent() { return new BlobTargetOption(StorageRpc.Option.IF_DISABLE_GZIP_CONTENT, true); } + /** + * Returns an option for detecting content type. If this option is used, the content type is + * detected from the blob name if not explicitly set. This option is on the client side only, it + * does not appear in a RPC call. + */ + public static BlobTargetOption detectContentType() { + return new BlobTargetOption(StorageRpc.Option.DETECT_CONTENT_TYPE, true); + } + /** * Returns an option to set a customer-supplied AES256 key for server-side encryption of the * blob. @@ -593,6 +602,7 @@ enum Option { CUSTOMER_SUPPLIED_KEY, KMS_KEY_NAME, USER_PROJECT, + DETECT_CONTENT_TYPE, IF_DISABLE_GZIP_CONTENT; StorageRpc.Option toRpcOption() { @@ -733,6 +743,15 @@ public static BlobWriteOption userProject(String userProject) { public static BlobWriteOption disableGzipContent() { return new BlobWriteOption(Option.IF_DISABLE_GZIP_CONTENT, true); } + + /** + * Returns an option for detecting content type. If this option is used, the content type is + * detected from the blob name if not explicitly set. This option is on the client side only, it + * does not appear in a RPC call. + */ + public static BlobWriteOption detectContentType() { + return new BlobWriteOption(Option.DETECT_CONTENT_TYPE, true); + } } /** Class for specifying blob source options. */ @@ -1832,9 +1851,10 @@ public static Builder newBuilder() { * Creates a new blob. Direct upload is used to upload {@code content}. For large content, {@link * #writer} is recommended as it uses resumable upload. MD5 and CRC32C hashes of {@code content} * are computed and used for validating transferred data. Accepts an optional userProject {@link - * BlobGetOption} option which defines the project id to assign operational costs. + * BlobGetOption} option which defines the project id to assign operational costs. The content + * type is detected from the blob name if not explicitly set. * - *

Example of creating a blob from a byte array. + *

Example of creating a blob from a byte array: * *

{@code
    * String bucketName = "my-unique-bucket";
@@ -1857,7 +1877,7 @@ public static Builder newBuilder() {
    * Accepts a userProject {@link BlobGetOption} option, which defines the project id to assign
    * operational costs.
    *
-   * 

Example of creating a blob from a byte array. + *

Example of creating a blob from a byte array: * *

{@code
    * String bucketName = "my-unique-bucket";
@@ -1876,7 +1896,7 @@ Blob create(
 
   /**
    * Creates a new blob. Direct upload is used to upload {@code content}. For large content, {@link
-   * #writer} is recommended as it uses resumable upload. By default any md5 and crc32c values in
+   * #writer} is recommended as it uses resumable upload. By default any MD5 and CRC32C values in
    * the given {@code blobInfo} are ignored unless requested via the {@code
    * BlobWriteOption.md5Match} and {@code BlobWriteOption.crc32cMatch} options. The given input
    * stream is closed upon success.
@@ -2603,11 +2623,11 @@ Blob createFrom(
   ReadChannel reader(BlobId blob, BlobSourceOption... options);
 
   /**
-   * Creates a blob and return a channel for writing its content. By default any md5 and crc32c
+   * Creates a blob and returns a channel for writing its content. By default any MD5 and CRC32C
    * values in the given {@code blobInfo} are ignored unless requested via the {@code
    * BlobWriteOption.md5Match} and {@code BlobWriteOption.crc32cMatch} options.
    *
-   * 

Example of writing a blob's content through a writer. + *

Example of writing a blob's content through a writer: * *

{@code
    * String bucketName = "my-unique-bucket";
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
index 519a9e9fa..ec849281e 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
@@ -77,9 +77,12 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
+import java.net.FileNameMap;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 public class HttpStorageRpc implements StorageRpc {
@@ -98,6 +101,7 @@ public class HttpStorageRpc implements StorageRpc {
   private final HttpRequestInitializer batchRequestInitializer;
 
   private static final long MEGABYTE = 1024L * 1024L;
+  private static final FileNameMap FILE_NAME_MAP = URLConnection.getFileNameMap();
 
   public HttpStorageRpc(StorageOptions options) {
     HttpTransportOptions transportOptions = (HttpTransportOptions) options.getTransportOptions();
@@ -286,7 +290,7 @@ public StorageObject create(
               .insert(
                   storageObject.getBucket(),
                   storageObject,
-                  new InputStreamContent(storageObject.getContentType(), content));
+                  new InputStreamContent(detectContentType(storageObject, options), content));
       insert.getMediaHttpUploader().setDirectUploadEnabled(true);
       Boolean disableGzipContent = Option.IF_DISABLE_GZIP_CONTENT.getBoolean(options);
       if (disableGzipContent != null) {
@@ -372,6 +376,19 @@ public Tuple> list(final String bucket, Map options) {
+    String contentType = object.getContentType();
+    if (contentType != null) {
+      return contentType;
+    }
+
+    if (Boolean.TRUE == Option.DETECT_CONTENT_TYPE.get(options)) {
+      contentType = FILE_NAME_MAP.getContentTypeFor(object.getName().toLowerCase(Locale.ENGLISH));
+    }
+
+    return firstNonNull(contentType, "application/octet-stream");
+  }
+
   private static Function objectFromPrefix(final String bucket) {
     return new Function() {
       @Override
@@ -834,9 +851,7 @@ public String open(StorageObject object, Map options) {
       HttpRequest httpRequest =
           requestFactory.buildPostRequest(url, new JsonHttpContent(jsonFactory, object));
       HttpHeaders requestHeaders = httpRequest.getHeaders();
-      requestHeaders.set(
-          "X-Upload-Content-Type",
-          firstNonNull(object.getContentType(), "application/octet-stream"));
+      requestHeaders.set("X-Upload-Content-Type", detectContentType(object, options));
       String key = Option.CUSTOMER_SUPPLIED_KEY.getString(options);
       if (key != null) {
         BaseEncoding base64 = BaseEncoding.base64();
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 7ae9c8ec1..d1249f6df 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
@@ -65,7 +65,8 @@ enum Option {
     KMS_KEY_NAME("kmsKeyName"),
     SERVICE_ACCOUNT_EMAIL("serviceAccount"),
     SHOW_DELETED_KEYS("showDeletedKeys"),
-    REQUESTED_POLICY_VERSION("optionsRequestedPolicyVersion");
+    REQUESTED_POLICY_VERSION("optionsRequestedPolicyVersion"),
+    DETECT_CONTENT_TYPE("detectContentType");
 
     private final String value;
 
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index ba4b3623f..fbce81db7 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -3363,4 +3363,61 @@ public void testUploadWithEncryption() throws Exception {
     byte[] readBytes = blob.getContent(Blob.BlobSourceOption.decryptionKey(KEY));
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
   }
+
+  private Blob createBlob(String method, BlobInfo blobInfo, boolean detectType) throws IOException {
+    switch (method) {
+      case "create":
+        return detectType
+            ? storage.create(blobInfo, Storage.BlobTargetOption.detectContentType())
+            : storage.create(blobInfo);
+      case "createFrom":
+        InputStream inputStream = new ByteArrayInputStream(BLOB_BYTE_CONTENT);
+        return detectType
+            ? storage.createFrom(blobInfo, inputStream, Storage.BlobWriteOption.detectContentType())
+            : storage.createFrom(blobInfo, inputStream);
+      case "writer":
+        if (detectType) {
+          storage.writer(blobInfo, Storage.BlobWriteOption.detectContentType()).close();
+        } else {
+          storage.writer(blobInfo).close();
+        }
+        return storage.get(BlobId.of(blobInfo.getBucket(), blobInfo.getName()));
+      default:
+        throw new IllegalArgumentException("Unknown method " + method);
+    }
+  }
+
+  private void testAutoContentType(String method) throws IOException {
+    String[] names = {"file1.txt", "dir with spaces/Pic.Jpg", "no_extension"};
+    String[] types = {"text/plain", "image/jpeg", "application/octet-stream"};
+    for (int i = 0; i < names.length; i++) {
+      BlobId blobId = BlobId.of(BUCKET, names[i]);
+      BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
+      Blob blob_true = createBlob(method, blobInfo, true);
+      assertEquals(types[i], blob_true.getContentType());
+
+      Blob blob_false = createBlob(method, blobInfo, false);
+      assertEquals("application/octet-stream", blob_false.getContentType());
+    }
+    String customType = "custom/type";
+    BlobId blobId = BlobId.of(BUCKET, names[0]);
+    BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(customType).build();
+    Blob blob = createBlob(method, blobInfo, true);
+    assertEquals(customType, blob.getContentType());
+  }
+
+  @Test
+  public void testAutoContentTypeCreate() throws IOException {
+    testAutoContentType("create");
+  }
+
+  @Test
+  public void testAutoContentTypeCreateFrom() throws IOException {
+    testAutoContentType("createFrom");
+  }
+
+  @Test
+  public void testAutoContentTypeWriter() throws IOException {
+    testAutoContentType("writer");
+  }
 }

From 9d4a57e7ecb89d89764ee5b70f64358637d9513e Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate 
Date: Thu, 16 Jul 2020 02:00:54 +0200
Subject: [PATCH 08/35] chore(deps): update dependency
 com.google.cloud:libraries-bom to v8.1.0 (#431)

---
 samples/snippets/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 320dca505..e1a723314 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
       
         com.google.cloud
         libraries-bom
-        8.0.0
+        8.1.0
         pom
         import
       

From 087d78c8a33802f2c88df1b4936ad56d78b09d3d Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate 
Date: Thu, 16 Jul 2020 19:50:41 +0200
Subject: [PATCH 09/35] build(deps): update dependency
 com.google.cloud:google-cloud-shared-config to v0.9.2 (#418)

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 853bd01e9..256dc9157 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
   
     com.google.cloud
     google-cloud-shared-config
-    0.9.0
+    0.9.2
   
 
   

From 38c1c34937eeacd126cf6d62bf85fb9db90e1702 Mon Sep 17 00:00:00 2001
From: Ajit Thakor <49403056+athakor@users.noreply.github.com>
Date: Sat, 18 Jul 2020 02:47:21 +0530
Subject: [PATCH 10/35] feat: add support of startOffset and endOffset (#430)

* feat: add support of startOffset and endOffset

* feat: add support of startOffset and endOffset
---
 .../com/google/cloud/storage/Storage.java     | 22 +++++++++
 .../cloud/storage/spi/v1/HttpStorageRpc.java  |  2 +
 .../cloud/storage/spi/v1/StorageRpc.java      |  2 +
 .../google/cloud/storage/StorageImplTest.java | 24 ++++++++++
 .../cloud/storage/it/ITStorageTest.java       | 47 +++++++++++++++++++
 5 files changed, 97 insertions(+)

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 202615cdd..82ba17508 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
@@ -1051,6 +1051,28 @@ public static BlobListOption delimiter(String delimiter) {
       return new BlobListOption(StorageRpc.Option.DELIMITER, delimiter);
     }
 
+    /**
+     * Returns an option to set a startOffset to filter results to objects whose names are
+     * lexicographically equal to or after startOffset. If endOffset is also set, the objects listed
+     * have names between startOffset (inclusive) and endOffset (exclusive).
+     *
+     * @param startOffset startOffset to filter the results
+     */
+    public static BlobListOption startOffset(String startOffset) {
+      return new BlobListOption(StorageRpc.Option.START_OFF_SET, startOffset);
+    }
+
+    /**
+     * Returns an option to set a endOffset to filter results to objects whose names are
+     * lexicographically before endOffset. If startOffset is also set, the objects listed have names
+     * between startOffset (inclusive) and endOffset (exclusive).
+     *
+     * @param endOffset endOffset to filter the results
+     */
+    public static BlobListOption endOffset(String endOffset) {
+      return new BlobListOption(StorageRpc.Option.END_OFF_SET, endOffset);
+    }
+
     /**
      * Returns an option to define the billing user project. This option is required by buckets with
      * `requester_pays` flag enabled to assign operation costs.
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
index ec849281e..d7b7baa0b 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
@@ -354,6 +354,8 @@ public Tuple> list(final String bucket, Map options =
+        ImmutableMap.of(
+            StorageRpc.Option.START_OFF_SET, startOffset, StorageRpc.Option.END_OFF_SET, endOffset);
+    ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2);
+    Tuple> result =
+        Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION));
+    EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, options)).andReturn(result);
+    EasyMock.replay(storageRpcMock);
+    initializeService();
+    ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2);
+    Page page =
+        storage.list(
+            BUCKET_NAME1,
+            Storage.BlobListOption.startOffset(startOffset),
+            Storage.BlobListOption.endOffset(endOffset));
+    assertEquals(cursor, page.getNextPageToken());
+    assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class));
+  }
+
   @Test
   public void testUpdateBucket() {
     BucketInfo updatedBucketInfo = BUCKET_INFO1.toBuilder().setIndexPage("some-page").build();
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index fbce81db7..22b1f1d43 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -993,6 +993,53 @@ public void testListBlobsVersioned() throws ExecutionException, InterruptedExcep
     }
   }
 
+  @Test
+  public void testListBlobsWithOffset() throws ExecutionException, InterruptedException {
+    String bucketName = RemoteStorageHelper.generateBucketName();
+    Bucket bucket =
+        storage.create(BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build());
+    try {
+      List blobNames =
+          ImmutableList.of("startOffset_blob1", "startOffset_blob2", "blob3_endOffset");
+      BlobInfo blob1 =
+          BlobInfo.newBuilder(bucket, blobNames.get(0)).setContentType(CONTENT_TYPE).build();
+      BlobInfo blob2 =
+          BlobInfo.newBuilder(bucket, blobNames.get(1)).setContentType(CONTENT_TYPE).build();
+      BlobInfo blob3 =
+          BlobInfo.newBuilder(bucket, blobNames.get(2)).setContentType(CONTENT_TYPE).build();
+
+      Blob remoteBlob1 = storage.create(blob1);
+      Blob remoteBlob2 = storage.create(blob2);
+      Blob remoteBlob3 = storage.create(blob3);
+      assertNotNull(remoteBlob1);
+      assertNotNull(remoteBlob2);
+      assertNotNull(remoteBlob3);
+
+      // Listing blobs without BlobListOptions.
+      Page page1 = storage.list(bucketName);
+      assertEquals(3, Iterators.size(page1.iterateAll().iterator()));
+
+      // Listing blobs with startOffset.
+      Page page2 =
+          storage.list(bucketName, Storage.BlobListOption.startOffset("startOffset"));
+      assertEquals(2, Iterators.size(page2.iterateAll().iterator()));
+
+      // Listing blobs with endOffset.
+      Page page3 = storage.list(bucketName, Storage.BlobListOption.endOffset("endOffset"));
+      assertEquals(1, Iterators.size(page3.iterateAll().iterator()));
+
+      // Listing blobs with startOffset and endOffset.
+      Page page4 =
+          storage.list(
+              bucketName,
+              Storage.BlobListOption.startOffset("startOffset"),
+              Storage.BlobListOption.endOffset("endOffset"));
+      assertEquals(0, Iterators.size(page4.iterateAll().iterator()));
+    } finally {
+      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+    }
+  }
+
   @Test(timeout = 5000)
   public void testListBlobsCurrentDirectory() throws InterruptedException {
     String directoryName = "test-list-blobs-current-directory/";

From e63feb7c684b9f5d4c9ec18edb33172b22bd01f0 Mon Sep 17 00:00:00 2001
From: Dmitry <58846611+dmitry-fa@users.noreply.github.com>
Date: Sat, 18 Jul 2020 09:53:38 +0300
Subject: [PATCH 11/35] test: ITStorageTest requires Permission
 'cloudkms.keyRings.setIamPolicy' (#387)

* test: ITStorageTest requires Permission 'cloudkms.keyRings.setIamPolicy'
* test: notice IT_SERVICE_ACCOUNT_EMAIL evn variable for running integration test
---
 CONTRIBUTING.md                                          | 1 +
 .../java/com/google/cloud/storage/it/ITStorageTest.java  | 9 ++++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f2dbdee06..39d1c88d9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,6 +48,7 @@ integration tests.
 
 ```bash
 export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json
+export IT_SERVICE_ACCOUNT_EMAIL=my-service-account@my-project.gserviceaccount.com
 mvn -Penable-integration-tests clean verify
 ```
 
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index 22b1f1d43..0f509271d 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -260,6 +260,7 @@ public HttpTransport create() {
   }
 
   private static void prepareKmsKeys() throws IOException {
+    // https://cloud.google.com/storage/docs/encryption/using-customer-managed-keys
     String projectId = remoteStorageHelper.getOptions().getProjectId();
     GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
     ManagedChannel kmsChannel =
@@ -339,7 +340,13 @@ private static void ensureKmsKeyRingIamPermissionsForTests(
             .build();
     requestParamsHeader.put(requestParamsKey, "parent=" + kmsKeyRingResourcePath);
     iamStub = MetadataUtils.attachHeaders(iamStub, requestParamsHeader);
-    iamStub.setIamPolicy(setIamPolicyRequest);
+    try {
+      iamStub.setIamPolicy(setIamPolicyRequest);
+    } catch (StatusRuntimeException e) {
+      if (log.isLoggable(Level.WARNING)) {
+        log.log(Level.WARNING, "Unable to set IAM policy: {0}", e.getMessage());
+      }
+    }
   }
 
   private static String ensureKmsKeyExistsForTests(

From f8a4b12517c661881d7b7c65f796c1c8f1cf3ae9 Mon Sep 17 00:00:00 2001
From: Ajit Thakor <49403056+athakor@users.noreply.github.com>
Date: Wed, 22 Jul 2020 22:17:00 +0530
Subject: [PATCH 12/35] feat: add support of null to remove the CORS
 configuration from bucket  (#438)

* feat: add support of null to remove bucket CORS configuration

* feat: addresse review changes
---
 .../com/google/cloud/storage/BucketInfo.java  |  2 +-
 .../com/google/cloud/storage/BucketTest.java  | 32 +++++++++++++
 .../cloud/storage/it/ITStorageTest.java       | 47 +++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)

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 9a716d702..361363638 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
@@ -1240,7 +1240,7 @@ Builder setMetageneration(Long metageneration) {
 
     @Override
     public Builder setCors(Iterable cors) {
-      this.cors = cors != null ? ImmutableList.copyOf(cors) : null;
+      this.cors = cors != null ? ImmutableList.copyOf(cors) : ImmutableList.of();
       return this;
     }
 
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
index 94050ffef..862fbb358 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
@@ -886,4 +886,36 @@ public void testUpdateBucketLogging() {
     assertThat(actualUpdatedBucket.getLogging().getLogBucket()).isNull();
     assertThat(actualUpdatedBucket.getLogging().getLogObjectPrefix()).isNull();
   }
+
+  @Test
+  public void testRemoveBucketCORS() {
+    initializeExpectedBucket(6);
+    List origins = ImmutableList.of(Cors.Origin.of("http://cloud.google.com"));
+    List httpMethods = ImmutableList.of(HttpMethod.GET);
+    List responseHeaders = ImmutableList.of("Content-Type");
+    Cors cors =
+        Cors.newBuilder()
+            .setOrigins(origins)
+            .setMethods(httpMethods)
+            .setResponseHeaders(responseHeaders)
+            .setMaxAgeSeconds(100)
+            .build();
+    BucketInfo bucketInfo = BucketInfo.newBuilder("b").setCors(ImmutableList.of(cors)).build();
+    Bucket bucket = new Bucket(serviceMockReturnsOptions, new BucketInfo.BuilderImpl(bucketInfo));
+    assertThat(bucket.getCors()).isNotNull();
+    assertThat(bucket.getCors().get(0).getMaxAgeSeconds()).isEqualTo(100);
+    assertThat(bucket.getCors().get(0).getMethods()).isEqualTo(httpMethods);
+    assertThat(bucket.getCors().get(0).getOrigins()).isEqualTo(origins);
+    assertThat(bucket.getCors().get(0).getResponseHeaders()).isEqualTo(responseHeaders);
+
+    // Remove bucket CORS configuration.
+    Bucket expectedUpdatedBucket = bucket.toBuilder().setCors(null).build();
+    expect(storage.getOptions()).andReturn(mockOptions).times(2);
+    expect(storage.update(expectedUpdatedBucket)).andReturn(expectedUpdatedBucket);
+    replay(storage);
+    initializeBucket();
+    Bucket updatedBucket = new Bucket(storage, new BucketInfo.BuilderImpl(expectedUpdatedBucket));
+    Bucket actualUpdatedBucket = updatedBucket.update();
+    assertThat(actualUpdatedBucket.getCors()).isEmpty();
+  }
 }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index 0f509271d..b2bbe54ed 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -66,6 +66,7 @@
 import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleAction;
 import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleCondition;
 import com.google.cloud.storage.CopyWriter;
+import com.google.cloud.storage.Cors;
 import com.google.cloud.storage.HmacKey;
 import com.google.cloud.storage.HttpMethod;
 import com.google.cloud.storage.PostPolicyV4;
@@ -3474,4 +3475,50 @@ public void testAutoContentTypeCreateFrom() throws IOException {
   public void testAutoContentTypeWriter() throws IOException {
     testAutoContentType("writer");
   }
+
+  @Test
+  public void testRemoveBucketCORS() throws ExecutionException, InterruptedException {
+    String bucketName = RemoteStorageHelper.generateBucketName();
+    List origins = ImmutableList.of(Cors.Origin.of("http://cloud.google.com"));
+    List httpMethods = ImmutableList.of(HttpMethod.GET);
+    List responseHeaders = ImmutableList.of("Content-Type");
+    try {
+      Cors cors =
+          Cors.newBuilder()
+              .setOrigins(origins)
+              .setMethods(httpMethods)
+              .setResponseHeaders(responseHeaders)
+              .setMaxAgeSeconds(100)
+              .build();
+      storage.create(BucketInfo.newBuilder(bucketName).setCors(ImmutableList.of(cors)).build());
+
+      // case-1 : Cors are set and field selector is selected then returns not-null.
+      Bucket remoteBucket =
+          storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
+      assertThat(remoteBucket.getCors()).isNotNull();
+      assertThat(remoteBucket.getCors().get(0).getMaxAgeSeconds()).isEqualTo(100);
+      assertThat(remoteBucket.getCors().get(0).getMethods()).isEqualTo(httpMethods);
+      assertThat(remoteBucket.getCors().get(0).getOrigins()).isEqualTo(origins);
+      assertThat(remoteBucket.getCors().get(0).getResponseHeaders()).isEqualTo(responseHeaders);
+
+      // case-2 : Cors are set but field selector isn't selected then returns not-null.
+      remoteBucket = storage.get(bucketName);
+      assertThat(remoteBucket.getCors()).isNotNull();
+
+      // Remove CORS configuration from the bucket.
+      Bucket updatedBucket = remoteBucket.toBuilder().setCors(null).build().update();
+      assertThat(updatedBucket.getCors()).isNull();
+
+      // case-3 : Cors are not set and field selector is selected then returns null.
+      updatedBucket = storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
+      assertThat(updatedBucket.getCors()).isNull();
+
+      // case-4 : Cors are not set and field selector isn't selected then returns null.
+      updatedBucket = storage.get(bucketName);
+      assertThat(updatedBucket.getCors()).isNull();
+
+    } finally {
+      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+    }
+  }
 }

From 0dcafc013612b66308033ffd821d8e8a26deba3c Mon Sep 17 00:00:00 2001
From: Yoshi Automation Bot 
Date: Fri, 31 Jul 2020 09:47:02 -0700
Subject: [PATCH 13/35] chore: update README.md with latest versions (#441)

---
 CONTRIBUTING.md |  1 -
 README.md       |  4 +--
 synth.metadata  | 78 ++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 39d1c88d9..f2dbdee06 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,7 +48,6 @@ integration tests.
 
 ```bash
 export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json
-export IT_SERVICE_ACCOUNT_EMAIL=my-service-account@my-project.gserviceaccount.com
 mvn -Penable-integration-tests clean verify
 ```
 
diff --git a/README.md b/README.md
index 230e23076..000f72b38 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
     
       com.google.cloud
       libraries-bom
-      7.0.0
+      8.1.0
       pom
       import
     
@@ -38,7 +38,7 @@ If you are using Maven without BOM, add this to your dependencies:
 
   com.google.cloud
   google-cloud-storage
-  1.111.0
+  1.111.2
 
 
 ```
diff --git a/synth.metadata b/synth.metadata
index ed7f3e53e..f19c30e80 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,7 +4,7 @@
       "git": {
         "name": ".",
         "remote": "https://github.com/googleapis/java-storage.git",
-        "sha": "a5773d4c43127e808ee56b7185e58af6c3cdb8a2"
+        "sha": "f8a4b12517c661881d7b7c65f796c1c8f1cf3ae9"
       }
     },
     {
@@ -14,5 +14,81 @@
         "sha": "4f2c9f752a94042472fc03c5bd9e06e89817d2bd"
       }
     }
+  ],
+  "generatedFiles": [
+    ".github/CODEOWNERS",
+    ".github/ISSUE_TEMPLATE/bug_report.md",
+    ".github/ISSUE_TEMPLATE/feature_request.md",
+    ".github/ISSUE_TEMPLATE/support_request.md",
+    ".github/PULL_REQUEST_TEMPLATE.md",
+    ".github/release-please.yml",
+    ".github/trusted-contribution.yml",
+    ".github/workflows/ci.yaml",
+    ".kokoro/build.bat",
+    ".kokoro/build.sh",
+    ".kokoro/coerce_logs.sh",
+    ".kokoro/common.cfg",
+    ".kokoro/common.sh",
+    ".kokoro/continuous/common.cfg",
+    ".kokoro/continuous/dependencies.cfg",
+    ".kokoro/continuous/integration.cfg",
+    ".kokoro/continuous/java11.cfg",
+    ".kokoro/continuous/java7.cfg",
+    ".kokoro/continuous/java8-osx.cfg",
+    ".kokoro/continuous/java8-win.cfg",
+    ".kokoro/continuous/java8.cfg",
+    ".kokoro/continuous/lint.cfg",
+    ".kokoro/continuous/propose_release.cfg",
+    ".kokoro/continuous/samples.cfg",
+    ".kokoro/dependencies.sh",
+    ".kokoro/linkage-monitor.sh",
+    ".kokoro/nightly/common.cfg",
+    ".kokoro/nightly/dependencies.cfg",
+    ".kokoro/nightly/integration.cfg",
+    ".kokoro/nightly/java11.cfg",
+    ".kokoro/nightly/java7.cfg",
+    ".kokoro/nightly/java8-osx.cfg",
+    ".kokoro/nightly/java8-win.cfg",
+    ".kokoro/nightly/java8.cfg",
+    ".kokoro/nightly/lint.cfg",
+    ".kokoro/nightly/samples.cfg",
+    ".kokoro/populate-secrets.sh",
+    ".kokoro/presubmit/clirr.cfg",
+    ".kokoro/presubmit/common.cfg",
+    ".kokoro/presubmit/dependencies.cfg",
+    ".kokoro/presubmit/java11.cfg",
+    ".kokoro/presubmit/java7.cfg",
+    ".kokoro/presubmit/java8-osx.cfg",
+    ".kokoro/presubmit/java8-win.cfg",
+    ".kokoro/presubmit/java8.cfg",
+    ".kokoro/presubmit/linkage-monitor.cfg",
+    ".kokoro/presubmit/lint.cfg",
+    ".kokoro/presubmit/samples.cfg",
+    ".kokoro/release/bump_snapshot.cfg",
+    ".kokoro/release/common.cfg",
+    ".kokoro/release/common.sh",
+    ".kokoro/release/drop.cfg",
+    ".kokoro/release/drop.sh",
+    ".kokoro/release/promote.cfg",
+    ".kokoro/release/promote.sh",
+    ".kokoro/release/publish_javadoc.cfg",
+    ".kokoro/release/publish_javadoc.sh",
+    ".kokoro/release/snapshot.cfg",
+    ".kokoro/release/snapshot.sh",
+    ".kokoro/release/stage.cfg",
+    ".kokoro/release/stage.sh",
+    ".kokoro/trampoline.sh",
+    "CODE_OF_CONDUCT.md",
+    "CONTRIBUTING.md",
+    "LICENSE",
+    "README.md",
+    "codecov.yaml",
+    "java.header",
+    "license-checks.xml",
+    "renovate.json",
+    "samples/install-without-bom/pom.xml",
+    "samples/pom.xml",
+    "samples/snapshot/pom.xml",
+    "samples/snippets/pom.xml"
   ]
 }
\ No newline at end of file

From 0ce82ccf1e6ed8f46aeda92fe5f17eb427c3a7d9 Mon Sep 17 00:00:00 2001
From: Dmitry <58846611+dmitry-fa@users.noreply.github.com>
Date: Fri, 31 Jul 2020 20:05:19 +0300
Subject: [PATCH 14/35] test: ITStorage cleanup (#439)

* test: ITStorageTest cleanup

* test: ITStorageTest cleanup
---
 .../cloud/storage/it/ITStorageTest.java       | 206 ++++++++++--------
 1 file changed, 110 insertions(+), 96 deletions(-)

diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index b2bbe54ed..59000a078 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -136,7 +136,6 @@
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.junit.AfterClass;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -151,6 +150,7 @@ public class ITStorageTest {
       Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER);
   private static final Logger log = Logger.getLogger(ITStorageTest.class.getName());
   private static final String BUCKET = RemoteStorageHelper.generateBucketName();
+  private static final String BUCKET_REQUESTER_PAYS = RemoteStorageHelper.generateBucketName();
   private static final String CONTENT_TYPE = "text/plain";
   private static final byte[] BLOB_BYTE_CONTENT = {0xD, 0xE, 0xA, 0xD};
   private static final String BLOB_STRING_CONTENT = "Hello Google Cloud Storage!";
@@ -213,15 +213,16 @@ public static void beforeClass() throws IOException {
                         LifecycleCondition.newBuilder().setAge(1).build())))
             .build());
 
+    storage.create(BucketInfo.newBuilder(BUCKET_REQUESTER_PAYS).build());
+
     // Prepare KMS KeyRing for CMEK tests
     prepareKmsKeys();
   }
 
-  @Before
-  public void beforeEach() {
+  private static void unsetRequesterPays() {
     Bucket remoteBucket =
         storage.get(
-            BUCKET,
+            BUCKET_REQUESTER_PAYS,
             Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
             Storage.BucketGetOption.userProject(storage.getOptions().getProjectId()));
     // Disable requester pays in case a test fails to clean up.
@@ -247,6 +248,8 @@ public static void afterClass() throws ExecutionException, InterruptedException
       if (!wasDeleted && log.isLoggable(Level.WARNING)) {
         log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET);
       }
+      unsetRequesterPays();
+      RemoteStorageHelper.forceDelete(storage, BUCKET_REQUESTER_PAYS, 5, TimeUnit.SECONDS);
     }
   }
 
@@ -913,22 +916,26 @@ public void testListBlobsEmptySelectedFields() throws InterruptedException {
 
   @Test(timeout = 7500)
   public void testListBlobRequesterPays() throws InterruptedException {
+    unsetRequesterPays();
     BlobInfo blob1 =
-        BlobInfo.newBuilder(BUCKET, "test-list-blobs-empty-selected-fields-blob1")
+        BlobInfo.newBuilder(BUCKET_REQUESTER_PAYS, "test-list-blobs-empty-selected-fields-blob1")
             .setContentType(CONTENT_TYPE)
             .build();
     assertNotNull(storage.create(blob1));
 
     // Test listing a Requester Pays bucket.
     Bucket remoteBucket =
-        storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
-    assertFalse(remoteBucket.requesterPays());
+        storage.get(
+            BUCKET_REQUESTER_PAYS,
+            Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+
+    assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
     remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
     Bucket updatedBucket = storage.update(remoteBucket);
     assertTrue(updatedBucket.requesterPays());
     try {
       storage.list(
-          BUCKET,
+          BUCKET_REQUESTER_PAYS,
           Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
           Storage.BlobListOption.fields(),
           Storage.BlobListOption.userProject("fakeBillingProjectId"));
@@ -941,7 +948,7 @@ public void testListBlobRequesterPays() throws InterruptedException {
     while (true) {
       Page page =
           storage.list(
-              BUCKET,
+              BUCKET_REQUESTER_PAYS,
               Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
               Storage.BlobListOption.fields(),
               Storage.BlobListOption.userProject(projectId));
@@ -2233,6 +2240,7 @@ public void testUpdateBlobsFail() {
 
   @Test
   public void testBucketAcl() {
+    unsetRequesterPays();
     testBucketAclRequesterPays(true);
     testBucketAclRequesterPays(false);
   }
@@ -2240,8 +2248,10 @@ public void testBucketAcl() {
   private void testBucketAclRequesterPays(boolean requesterPays) {
     if (requesterPays) {
       Bucket remoteBucket =
-          storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
-      assertNull(remoteBucket.requesterPays());
+          storage.get(
+              BUCKET_REQUESTER_PAYS,
+              Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+      assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
       remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
       Bucket updatedBucket = storage.update(remoteBucket);
       assertTrue(updatedBucket.requesterPays());
@@ -2254,22 +2264,27 @@ private void testBucketAclRequesterPays(boolean requesterPays) {
             ? new Storage.BucketSourceOption[] {Storage.BucketSourceOption.userProject(projectId)}
             : new Storage.BucketSourceOption[] {};
 
-    assertNull(storage.getAcl(BUCKET, User.ofAllAuthenticatedUsers(), bucketOptions));
-    assertFalse(storage.deleteAcl(BUCKET, User.ofAllAuthenticatedUsers(), bucketOptions));
+    assertNull(
+        storage.getAcl(BUCKET_REQUESTER_PAYS, User.ofAllAuthenticatedUsers(), bucketOptions));
+    assertFalse(
+        storage.deleteAcl(BUCKET_REQUESTER_PAYS, User.ofAllAuthenticatedUsers(), bucketOptions));
     Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
-    assertNotNull(storage.createAcl(BUCKET, acl, bucketOptions));
+    assertNotNull(storage.createAcl(BUCKET_REQUESTER_PAYS, acl, bucketOptions));
     Acl updatedAcl =
-        storage.updateAcl(BUCKET, acl.toBuilder().setRole(Role.WRITER).build(), bucketOptions);
+        storage.updateAcl(
+            BUCKET_REQUESTER_PAYS, acl.toBuilder().setRole(Role.WRITER).build(), bucketOptions);
     assertEquals(Role.WRITER, updatedAcl.getRole());
     Set acls = new HashSet<>();
-    acls.addAll(storage.listAcls(BUCKET, bucketOptions));
+    acls.addAll(storage.listAcls(BUCKET_REQUESTER_PAYS, bucketOptions));
     assertTrue(acls.contains(updatedAcl));
-    assertTrue(storage.deleteAcl(BUCKET, User.ofAllAuthenticatedUsers(), bucketOptions));
-    assertNull(storage.getAcl(BUCKET, User.ofAllAuthenticatedUsers(), bucketOptions));
+    assertTrue(
+        storage.deleteAcl(BUCKET_REQUESTER_PAYS, User.ofAllAuthenticatedUsers(), bucketOptions));
+    assertNull(
+        storage.getAcl(BUCKET_REQUESTER_PAYS, User.ofAllAuthenticatedUsers(), bucketOptions));
     if (requesterPays) {
       Bucket remoteBucket =
           storage.get(
-              BUCKET,
+              BUCKET_REQUESTER_PAYS,
               Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
               Storage.BucketGetOption.userProject(projectId));
       assertTrue(remoteBucket.requesterPays());
@@ -2461,80 +2476,75 @@ public void testReadCompressedBlob() throws IOException {
 
   @Test
   public void testBucketPolicyV1RequesterPays() throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
-    storage.create(BucketInfo.newBuilder(bucketName).build());
+    unsetRequesterPays();
+    Bucket bucketDefault =
+        storage.get(
+            BUCKET_REQUESTER_PAYS,
+            Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+    assertTrue(bucketDefault.requesterPays() == null || !bucketDefault.requesterPays());
 
-    try {
-      Bucket bucketDefault =
-          storage.get(
-              bucketName, Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
-      assertNull(bucketDefault.requesterPays());
-
-      Bucket bucketTrue = storage.update(bucketDefault.toBuilder().setRequesterPays(true).build());
-      assertTrue(bucketTrue.requesterPays());
-
-      String projectId = remoteStorageHelper.getOptions().getProjectId();
-
-      Storage.BucketSourceOption[] bucketOptions =
-          new Storage.BucketSourceOption[] {Storage.BucketSourceOption.userProject(projectId)};
-      Identity projectOwner = Identity.projectOwner(projectId);
-      Identity projectEditor = Identity.projectEditor(projectId);
-      Identity projectViewer = Identity.projectViewer(projectId);
-      Map> bindingsWithoutPublicRead =
-          ImmutableMap.of(
-              StorageRoles.legacyBucketOwner(),
-              new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
-              StorageRoles.legacyBucketReader(),
-              (Set) new HashSet<>(Collections.singleton(projectViewer)));
-      Map> bindingsWithPublicRead =
-          ImmutableMap.of(
-              StorageRoles.legacyBucketOwner(),
-              new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
-              StorageRoles.legacyBucketReader(),
-              new HashSet<>(Collections.singleton(projectViewer)),
-              StorageRoles.legacyObjectReader(),
-              (Set) new HashSet<>(Collections.singleton(Identity.allUsers())));
-
-      // Validate getting policy.
-      Policy currentPolicy = storage.getIamPolicy(bucketName, bucketOptions);
-      assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
-
-      // Validate updating policy.
-      Policy updatedPolicy =
-          storage.setIamPolicy(
-              bucketName,
-              currentPolicy
-                  .toBuilder()
-                  .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
-                  .build(),
-              bucketOptions);
-      assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
-      Policy revertedPolicy =
-          storage.setIamPolicy(
-              bucketName,
-              updatedPolicy
-                  .toBuilder()
-                  .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
-                  .build(),
-              bucketOptions);
-      assertEquals(bindingsWithoutPublicRead, revertedPolicy.getBindings());
-
-      // Validate testing permissions.
-      List expectedPermissions = ImmutableList.of(true, true);
-      assertEquals(
-          expectedPermissions,
-          storage.testIamPermissions(
-              bucketName,
-              ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
-              bucketOptions));
-      Bucket bucketFalse =
-          storage.update(
-              bucketTrue.toBuilder().setRequesterPays(false).build(),
-              Storage.BucketTargetOption.userProject(projectId));
-      assertFalse(bucketFalse.requesterPays());
-    } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
-    }
+    Bucket bucketTrue = storage.update(bucketDefault.toBuilder().setRequesterPays(true).build());
+    assertTrue(bucketTrue.requesterPays());
+
+    String projectId = remoteStorageHelper.getOptions().getProjectId();
+
+    Storage.BucketSourceOption[] bucketOptions =
+        new Storage.BucketSourceOption[] {Storage.BucketSourceOption.userProject(projectId)};
+    Identity projectOwner = Identity.projectOwner(projectId);
+    Identity projectEditor = Identity.projectEditor(projectId);
+    Identity projectViewer = Identity.projectViewer(projectId);
+    Map> bindingsWithoutPublicRead =
+        ImmutableMap.of(
+            StorageRoles.legacyBucketOwner(),
+            new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
+            StorageRoles.legacyBucketReader(),
+            (Set) new HashSet<>(Collections.singleton(projectViewer)));
+    Map> bindingsWithPublicRead =
+        ImmutableMap.of(
+            StorageRoles.legacyBucketOwner(),
+            new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
+            StorageRoles.legacyBucketReader(),
+            new HashSet<>(Collections.singleton(projectViewer)),
+            StorageRoles.legacyObjectReader(),
+            (Set) new HashSet<>(Collections.singleton(Identity.allUsers())));
+
+    // Validate getting policy.
+    Policy currentPolicy = storage.getIamPolicy(BUCKET_REQUESTER_PAYS, bucketOptions);
+    assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
+
+    // Validate updating policy.
+    Policy updatedPolicy =
+        storage.setIamPolicy(
+            BUCKET_REQUESTER_PAYS,
+            currentPolicy
+                .toBuilder()
+                .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+                .build(),
+            bucketOptions);
+    assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
+    Policy revertedPolicy =
+        storage.setIamPolicy(
+            BUCKET_REQUESTER_PAYS,
+            updatedPolicy
+                .toBuilder()
+                .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+                .build(),
+            bucketOptions);
+    assertEquals(bindingsWithoutPublicRead, revertedPolicy.getBindings());
+
+    // Validate testing permissions.
+    List expectedPermissions = ImmutableList.of(true, true);
+    assertEquals(
+        expectedPermissions,
+        storage.testIamPermissions(
+            BUCKET_REQUESTER_PAYS,
+            ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
+            bucketOptions));
+    Bucket bucketFalse =
+        storage.update(
+            bucketTrue.toBuilder().setRequesterPays(false).build(),
+            Storage.BucketTargetOption.userProject(projectId));
+    assertFalse(bucketFalse.requesterPays());
   }
 
   @Test
@@ -2764,9 +2774,12 @@ public void testUpdateBucketLabel() {
 
   @Test
   public void testUpdateBucketRequesterPays() {
+    unsetRequesterPays();
     Bucket remoteBucket =
-        storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
-    assertFalse(remoteBucket.requesterPays());
+        storage.get(
+            BUCKET_REQUESTER_PAYS,
+            Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+    assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
     remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
     Bucket updatedBucket = storage.update(remoteBucket);
     assertTrue(updatedBucket.requesterPays());
@@ -2777,7 +2790,8 @@ public void testUpdateBucketRequesterPays() {
     Blob remoteBlob = updatedBucket.create(blobName, BLOB_BYTE_CONTENT, option);
     assertNotNull(remoteBlob);
     byte[] readBytes =
-        storage.readAllBytes(BUCKET, blobName, Storage.BlobSourceOption.userProject(projectId));
+        storage.readAllBytes(
+            BUCKET_REQUESTER_PAYS, blobName, Storage.BlobSourceOption.userProject(projectId));
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
     remoteBucket = remoteBucket.toBuilder().setRequesterPays(false).build();
     updatedBucket = storage.update(remoteBucket, Storage.BucketTargetOption.userProject(projectId));
@@ -3023,7 +3037,7 @@ public void testAttemptDeletionObjectTemporaryHold() {
     } catch (StorageException ex) {
       // expected
     } finally {
-      remoteBlob.toBuilder().setEventBasedHold(false).build().update();
+      remoteBlob.toBuilder().setTemporaryHold(false).build().update();
     }
   }
 

From bdacdc93a107108add5bd9dc00473997534aa761 Mon Sep 17 00:00:00 2001
From: Frank Natividad 
Date: Tue, 4 Aug 2020 15:58:27 -0700
Subject: [PATCH 15/35] fix: Ignore CONTRIBUTING.md (#447)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [x] Make sure to open an issue as a #446 before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)

Fixes #446 ☕️
---
 CONTRIBUTING.md | 1 +
 synth.py        | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f2dbdee06..39d1c88d9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,6 +48,7 @@ integration tests.
 
 ```bash
 export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json
+export IT_SERVICE_ACCOUNT_EMAIL=my-service-account@my-project.gserviceaccount.com
 mvn -Penable-integration-tests clean verify
 ```
 
diff --git a/synth.py b/synth.py
index 7e8a9a3dc..23c93c7c2 100644
--- a/synth.py
+++ b/synth.py
@@ -19,6 +19,7 @@
 AUTOSYNTH_MULTIPLE_COMMITS = True
 
 java.common_templates(excludes=[
-  '.kokoro/presubmit/integration.cfg'
+  '.kokoro/presubmit/integration.cfg',
+  'CONTRIBUTING.md'
 ])
 

From 12bc02d7bc05e584cad4362628155333630fbcba Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate 
Date: Wed, 5 Aug 2020 20:18:06 +0200
Subject: [PATCH 16/35] deps: update dependency
 com.google.cloud:google-cloud-shared-dependencies to v0.8.4 (#452)

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 256dc9157..ba8360717 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
       
         com.google.cloud
         google-cloud-shared-dependencies
-        0.8.3
+        0.8.4
         pom
         import
       

From f0e945e14662b86594298557b83151d3cb7e1ebb Mon Sep 17 00:00:00 2001
From: Ajit Thakor <49403056+athakor@users.noreply.github.com>
Date: Wed, 5 Aug 2020 23:49:39 +0530
Subject: [PATCH 17/35] feat: expose updateTime field of the bucket (#449)

* feat: expose updateTime field of the bucket

* fix: package imports

* feat: make the bucket fields output only

* fix: address review changes

* feat: add more checks

* feat: remove redundant checks
---
 .../clirr-ignored-differences.xml             |  5 ++++
 .../java/com/google/cloud/storage/Bucket.java |  6 +++++
 .../com/google/cloud/storage/BucketInfo.java  | 26 +++++++++++++++++++
 .../google/cloud/storage/BucketInfoTest.java  |  6 +++++
 .../com/google/cloud/storage/BucketTest.java  |  4 +++
 .../cloud/storage/it/ITStorageTest.java       | 22 ++++++++++++++++
 6 files changed, 69 insertions(+)

diff --git a/google-cloud-storage/clirr-ignored-differences.xml b/google-cloud-storage/clirr-ignored-differences.xml
index bca2faaff..114310c67 100644
--- a/google-cloud-storage/clirr-ignored-differences.xml
+++ b/google-cloud-storage/clirr-ignored-differences.xml
@@ -21,4 +21,9 @@
         com.google.cloud.storage.BucketInfo$Builder deleteLifecycleRules()
         7013
     
+    
+        com/google/cloud/storage/BucketInfo$Builder
+        com.google.cloud.storage.BucketInfo$Builder setUpdateTime(java.lang.Long)
+        7013
+    
 
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
index c64a3dc5e..3714e6e94 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
@@ -601,6 +601,12 @@ Builder setCreateTime(Long createTime) {
       return this;
     }
 
+    @Override
+    Builder setUpdateTime(Long updateTime) {
+      infoBuilder.setUpdateTime(updateTime);
+      return this;
+    }
+
     @Override
     Builder setMetageneration(Long metageneration) {
       infoBuilder.setMetageneration(metageneration);
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 361363638..b3e96113b 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
@@ -84,6 +84,7 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo)
   private final List lifecycleRules;
   private final String etag;
   private final Long createTime;
+  private final Long updateTime;
   private final Long metageneration;
   private final List cors;
   private final List acl;
@@ -1005,6 +1006,8 @@ public abstract static class Builder {
 
     abstract Builder setCreateTime(Long createTime);
 
+    abstract Builder setUpdateTime(Long updateTime);
+
     abstract Builder setMetageneration(Long metageneration);
 
     abstract Builder setLocationType(String locationType);
@@ -1090,6 +1093,7 @@ static final class BuilderImpl extends Builder {
     private String location;
     private String etag;
     private Long createTime;
+    private Long updateTime;
     private Long metageneration;
     private List cors;
     private List acl;
@@ -1113,6 +1117,7 @@ static final class BuilderImpl extends Builder {
       name = bucketInfo.name;
       etag = bucketInfo.etag;
       createTime = bucketInfo.createTime;
+      updateTime = bucketInfo.updateTime;
       metageneration = bucketInfo.metageneration;
       location = bucketInfo.location;
       storageClass = bucketInfo.storageClass;
@@ -1232,6 +1237,12 @@ Builder setCreateTime(Long createTime) {
       return this;
     }
 
+    @Override
+    Builder setUpdateTime(Long updateTime) {
+      this.updateTime = updateTime;
+      return this;
+    }
+
     @Override
     Builder setMetageneration(Long metageneration) {
       this.metageneration = metageneration;
@@ -1337,6 +1348,7 @@ public BucketInfo build() {
     name = builder.name;
     etag = builder.etag;
     createTime = builder.createTime;
+    updateTime = builder.updateTime;
     metageneration = builder.metageneration;
     location = builder.location;
     storageClass = builder.storageClass;
@@ -1468,6 +1480,14 @@ public Long getCreateTime() {
     return createTime;
   }
 
+  /**
+   * Returns the last modification time of the bucket's metadata expressed as the number of
+   * milliseconds since the Unix epoch.
+   */
+  public Long getUpdateTime() {
+    return updateTime;
+  }
+
   /** Returns the metadata generation of this bucket. */
   public Long getMetageneration() {
     return metageneration;
@@ -1650,6 +1670,9 @@ com.google.api.services.storage.model.Bucket toPb() {
     if (createTime != null) {
       bucketPb.setTimeCreated(new DateTime(createTime));
     }
+    if (updateTime != null) {
+      bucketPb.setUpdated(new DateTime(updateTime));
+    }
     if (metageneration != null) {
       bucketPb.setMetageneration(metageneration);
     }
@@ -1797,6 +1820,9 @@ static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb)
     if (bucketPb.getTimeCreated() != null) {
       builder.setCreateTime(bucketPb.getTimeCreated().getValue());
     }
+    if (bucketPb.getUpdated() != null) {
+      builder.setUpdateTime(bucketPb.getUpdated().getValue());
+    }
     if (bucketPb.getLocation() != null) {
       builder.setLocation(bucketPb.getLocation());
     }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
index b10839426..425cfb702 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
@@ -56,6 +56,7 @@ public class BucketInfoTest {
   private static final User OWNER = new User("user@gmail.com");
   private static final String SELF_LINK = "http://storage/b/n";
   private static final Long CREATE_TIME = System.currentTimeMillis();
+  private static final Long UPDATE_TIME = CREATE_TIME;
   private static final List CORS = Collections.singletonList(Cors.newBuilder().build());
   private static final List DEFAULT_ACL =
       Collections.singletonList(Acl.of(User.ofAllAuthenticatedUsers(), Role.WRITER));
@@ -117,6 +118,7 @@ public class BucketInfoTest {
           .setSelfLink(SELF_LINK)
           .setCors(CORS)
           .setCreateTime(CREATE_TIME)
+          .setUpdateTime(UPDATE_TIME)
           .setDefaultAcl(DEFAULT_ACL)
           .setDeleteRules(DELETE_RULES)
           .setLifecycleRules(LIFECYCLE_RULES)
@@ -148,6 +150,7 @@ public class BucketInfoTest {
           .setSelfLink(SELF_LINK)
           .setCors(CORS)
           .setCreateTime(CREATE_TIME)
+          .setUpdateTime(UPDATE_TIME)
           .setDefaultAcl(DEFAULT_ACL)
           .setDeleteRules(DELETE_RULES)
           .setLifecycleRules(LIFECYCLE_RULES)
@@ -202,6 +205,7 @@ public void testBuilder() {
     assertEquals(OWNER, BUCKET_INFO.getOwner());
     assertEquals(SELF_LINK, BUCKET_INFO.getSelfLink());
     assertEquals(CREATE_TIME, BUCKET_INFO.getCreateTime());
+    assertEquals(UPDATE_TIME, BUCKET_INFO.getUpdateTime());
     assertEquals(CORS, BUCKET_INFO.getCors());
     assertEquals(DEFAULT_ACL, BUCKET_INFO.getDefaultAcl());
     assertEquals(DELETE_RULES, BUCKET_INFO.getDeleteRules());
@@ -223,6 +227,7 @@ public void testBuilder() {
   }
 
   @Test
+  @SuppressWarnings({"unchecked", "deprecation"})
   public void testToPbAndFromPb() {
     compareBuckets(BUCKET_INFO, BucketInfo.fromPb(BUCKET_INFO.toPb()));
     BucketInfo bucketInfo =
@@ -245,6 +250,7 @@ private void compareBuckets(BucketInfo expected, BucketInfo value) {
     assertEquals(expected.getOwner(), value.getOwner());
     assertEquals(expected.getSelfLink(), value.getSelfLink());
     assertEquals(expected.getCreateTime(), value.getCreateTime());
+    assertEquals(expected.getUpdateTime(), value.getUpdateTime());
     assertEquals(expected.getCors(), value.getCors());
     assertEquals(expected.getDefaultAcl(), value.getDefaultAcl());
     assertEquals(expected.getDeleteRules(), value.getDeleteRules());
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
index 862fbb358..e8a8e3f46 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java
@@ -69,6 +69,7 @@ public class BucketTest {
   private static final User OWNER = new User("user@gmail.com");
   private static final String SELF_LINK = "http://storage/b/n";
   private static final Long CREATE_TIME = System.currentTimeMillis();
+  private static final Long UPDATE_TIME = CREATE_TIME - 1L;
   private static final List CORS = Collections.singletonList(Cors.newBuilder().build());
   private static final List DEFAULT_ACL =
       Collections.singletonList(Acl.of(User.ofAllAuthenticatedUsers(), WRITER));
@@ -111,6 +112,7 @@ public class BucketTest {
           .setSelfLink(SELF_LINK)
           .setCors(CORS)
           .setCreateTime(CREATE_TIME)
+          .setUpdateTime(UPDATE_TIME)
           .setDefaultAcl(DEFAULT_ACL)
           .setDeleteRules(DELETE_RULES)
           .setLifecycleRules(LIFECYCLE_RULES)
@@ -803,6 +805,7 @@ public void testBuilder() {
             .setSelfLink(SELF_LINK)
             .setCors(CORS)
             .setCreateTime(CREATE_TIME)
+            .setUpdateTime(UPDATE_TIME)
             .setDefaultAcl(DEFAULT_ACL)
             .setDeleteRules(DELETE_RULES)
             .setLifecycleRules(LIFECYCLE_RULES)
@@ -828,6 +831,7 @@ public void testBuilder() {
     assertEquals(OWNER, bucket.getOwner());
     assertEquals(SELF_LINK, bucket.getSelfLink());
     assertEquals(CREATE_TIME, bucket.getCreateTime());
+    assertEquals(UPDATE_TIME, bucket.getUpdateTime());
     assertEquals(CORS, bucket.getCors());
     assertEquals(DEFAULT_ACL, bucket.getDefaultAcl());
     assertEquals(DELETE_RULES, bucket.getDeleteRules());
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index 59000a078..a741f3eb7 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -3535,4 +3535,26 @@ public void testRemoveBucketCORS() throws ExecutionException, InterruptedExcepti
       RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
     }
   }
+
+  @Test
+  public void testBucketUpdateTime() throws ExecutionException, InterruptedException {
+    String bucketName = RemoteStorageHelper.generateBucketName();
+    BucketInfo bucketInfo =
+        BucketInfo.newBuilder(bucketName).setLocation("us").setVersioningEnabled(true).build();
+    try {
+      Bucket bucket = storage.create(bucketInfo);
+      assertThat(bucket).isNotNull();
+      assertThat(bucket.versioningEnabled()).isTrue();
+      assertThat(bucket.getCreateTime()).isNotNull();
+      assertThat(bucket.getUpdateTime()).isEqualTo(bucket.getCreateTime());
+
+      Bucket updatedBucket = bucket.toBuilder().setVersioningEnabled(false).build().update();
+      assertThat(updatedBucket.versioningEnabled()).isFalse();
+      assertThat(updatedBucket.getUpdateTime()).isNotNull();
+      assertThat(updatedBucket.getCreateTime()).isEqualTo(bucket.getCreateTime());
+      assertThat(updatedBucket.getUpdateTime()).isGreaterThan(bucket.getCreateTime());
+    } finally {
+      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+    }
+  }
 }

From 2f16e18b5a05b960ae8669431e624e4d36e8144f Mon Sep 17 00:00:00 2001
From: Yoshi Automation Bot 
Date: Fri, 7 Aug 2020 09:01:43 -0700
Subject: [PATCH 18/35] chore(docs): add cloud-RAD for Java (#444)

Co-authored-by: Jeff Ching 

Source-Author: Les Vogel 
Source-Date: Thu Jul 30 13:09:50 2020 -0700
Source-Repo: googleapis/synthtool
Source-Sha: dd230c816f88d0141fcd0be83498986287220d1b
Source-Link: https://github.com/googleapis/synthtool/commit/dd230c816f88d0141fcd0be83498986287220d1b
---
 .kokoro/release/publish_javadoc.cfg | 10 ++++++++++
 .kokoro/release/publish_javadoc.sh  | 25 +++++++++++++++++++++++--
 synth.metadata                      |  4 ++--
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg
index 82745e582..c4cb55210 100644
--- a/.kokoro/release/publish_javadoc.cfg
+++ b/.kokoro/release/publish_javadoc.cfg
@@ -1,14 +1,24 @@
 # Format: //devtools/kokoro/config/proto/build.proto
+
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/doc-templates/"
+
 env_vars: {
   key: "STAGING_BUCKET"
   value: "docs-staging"
 }
 
+env_vars: {
+  key: "STAGING_BUCKET_V2"
+  value: "docs-staging-v2-staging"
+  # Production will be at: docs-staging-v2
+}
+
 env_vars: {
   key: "TRAMPOLINE_BUILD_FILE"
   value: "github/java-storage/.kokoro/release/publish_javadoc.sh"
 }
 
+
 before_action {
   fetch_keystore {
     keystore_resource {
diff --git a/.kokoro/release/publish_javadoc.sh b/.kokoro/release/publish_javadoc.sh
index 219d37c60..6b5920141 100755
--- a/.kokoro/release/publish_javadoc.sh
+++ b/.kokoro/release/publish_javadoc.sh
@@ -24,6 +24,11 @@ if [[ -z "${STAGING_BUCKET}" ]]; then
   exit 1
 fi
 
+if [[ -z "${STAGING_BUCKET_V2}" ]]; then
+  echo "Need to set STAGING_BUCKET_V2 environment variable"
+  exit 1
+fi
+
 # work from the git root directory
 pushd $(dirname "$0")/../../
 
@@ -31,13 +36,13 @@ pushd $(dirname "$0")/../../
 python3 -m pip install gcp-docuploader
 
 # compile all packages
-mvn clean install -B -DskipTests=true
+mvn clean install -B -q -DskipTests=true
 
 NAME=google-cloud-storage
 VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3)
 
 # build the docs
-mvn site -B
+mvn site -B -q
 
 pushd target/site/apidocs
 
@@ -53,3 +58,19 @@ python3 -m docuploader upload . \
   --staging-bucket ${STAGING_BUCKET}
 
 popd
+
+# V2
+mvn clean site -B -q -Ddevsite.template="${KOKORO_GFILE_DIR}/java/"
+
+pushd target/devsite
+
+# create metadata
+python3 -m docuploader create-metadata \
+  --name ${NAME} \
+  --version ${VERSION} \
+  --language java
+
+# upload docs
+python3 -m docuploader upload . \
+  --credentials ${CREDENTIALS} \
+  --staging-bucket ${STAGING_BUCKET_V2}
diff --git a/synth.metadata b/synth.metadata
index f19c30e80..da1556f25 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,14 +4,14 @@
       "git": {
         "name": ".",
         "remote": "https://github.com/googleapis/java-storage.git",
-        "sha": "f8a4b12517c661881d7b7c65f796c1c8f1cf3ae9"
+        "sha": "0ce82ccf1e6ed8f46aeda92fe5f17eb427c3a7d9"
       }
     },
     {
       "git": {
         "name": "synthtool",
         "remote": "https://github.com/googleapis/synthtool.git",
-        "sha": "4f2c9f752a94042472fc03c5bd9e06e89817d2bd"
+        "sha": "dd230c816f88d0141fcd0be83498986287220d1b"
       }
     }
   ],

From f8d6e158a06aec926fb7bc42f10483d56696a37e Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate 
Date: Fri, 7 Aug 2020 23:34:03 +0200
Subject: [PATCH 19/35] deps: update dependency
 com.google.cloud:google-cloud-shared-dependencies to v0.8.6 (#458)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [com.google.cloud:google-cloud-shared-dependencies](https://togithub.com/googleapis/java-shared-dependencies) | patch | `0.8.4` -> `0.8.6` |

---

### Release Notes

googleapis/java-shared-dependencies ### [`v0.8.6`](https://togithub.com/googleapis/java-shared-dependencies/blob/master/CHANGELOG.md#​086-httpswwwgithubcomgoogleapisjava-shared-dependenciescomparev085v086-2020-08-07) [Compare Source](https://togithub.com/googleapis/java-shared-dependencies/compare/v0.8.5...v0.8.6) ### [`v0.8.5`](https://togithub.com/googleapis/java-shared-dependencies/blob/master/CHANGELOG.md#​085-httpswwwgithubcomgoogleapisjava-shared-dependenciescomparev084v085-2020-08-07) [Compare Source](https://togithub.com/googleapis/java-shared-dependencies/compare/v0.8.4...v0.8.5)
--- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#googleapis/java-storage). --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ba8360717..5e5031c56 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ com.google.cloud google-cloud-shared-dependencies - 0.8.4 + 0.8.6 pom import From f1c9331e396810fddbaedc6cc292506ee93a1abb Mon Sep 17 00:00:00 2001 From: Dmitry <58846611+dmitry-fa@users.noreply.github.com> Date: Wed, 12 Aug 2020 22:14:24 +0300 Subject: [PATCH 20/35] test: conversion easymock to mockito (#462) test: conversion easymock to mockito --- .../cloud/storage/StorageImplMockitoTest.java | 329 +++++++++++++++++- .../google/cloud/storage/StorageImplTest.java | 264 -------------- 2 files changed, 327 insertions(+), 266 deletions(-) diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java index 9eaefc66f..4efdda4d4 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -27,6 +28,7 @@ import static org.mockito.Mockito.mock; import com.google.api.core.ApiClock; +import com.google.api.gax.paging.Page; import com.google.api.services.storage.model.StorageObject; import com.google.cloud.Identity; import com.google.cloud.Policy; @@ -38,6 +40,7 @@ import com.google.cloud.storage.spi.v1.StorageRpc; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; import com.google.common.io.BaseEncoding; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -938,8 +941,6 @@ public void testCreateBlobFromStreamWithEncryptionKey() throws IOException { @Test @SuppressWarnings({"unchecked", "deprecation"}) public void testCreateBlobFromStreamRetryableException() throws IOException { - ArgumentCaptor capturedStream = - ArgumentCaptor.forClass(ByteArrayInputStream.class); ByteArrayInputStream fileStream = new ByteArrayInputStream(BLOB_CONTENT); @@ -1144,6 +1145,330 @@ public void testCreateFromMultipleParts() throws Exception { assertEquals(Blob.fromPb(storage, storageObject), blob); } + @Test + public void testListBuckets() { + String cursor = "cursor"; + ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); + + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(EMPTY_RPC_OPTIONS); + + initializeService(); + ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); + Page page = storage.list(); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); + } + + @Test + public void testListBucketsEmpty() { + doReturn(Tuple.>of(null, null)) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(EMPTY_RPC_OPTIONS); + + initializeService(); + Page page = storage.list(); + assertNull(page.getNextPageToken()); + assertArrayEquals( + ImmutableList.of().toArray(), Iterables.toArray(page.getValues(), Bucket.class)); + } + + @Test + public void testListBucketsWithOptions() { + String cursor = "cursor"; + ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); + + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_LIST_OPTIONS); + + initializeService(); + ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); + Page page = storage.list(BUCKET_LIST_PAGE_SIZE, BUCKET_LIST_PREFIX); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); + } + + @Test + public void testListBucketsWithSelectedFields() { + String cursor = "cursor"; + ArgumentCaptor> capturedOptions = + ArgumentCaptor.forClass(Map.class); + + ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); + + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(capturedOptions.capture()); + initializeService(); + ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); + Page page = storage.list(BUCKET_LIST_FIELDS); + String selector = (String) capturedOptions.getValue().get(BUCKET_LIST_FIELDS.getRpcOption()); + assertTrue(selector.contains("items(")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("acl")); + assertTrue(selector.contains("location")); + assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); + assertEquals(38, selector.length()); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); + } + + @Test + public void testListBucketsWithEmptyFields() { + String cursor = "cursor"; + ArgumentCaptor> capturedOptions = + ArgumentCaptor.forClass(Map.class); + ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); + + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(capturedOptions.capture()); + initializeService(); + ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); + Page page = storage.list(BUCKET_LIST_EMPTY_FIELDS); + String selector = + (String) capturedOptions.getValue().get(BUCKET_LIST_EMPTY_FIELDS.getRpcOption()); + assertTrue(selector.contains("items(")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); + assertEquals(25, selector.length()); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); + } + + @Test + public void testListBucketsWithException() { + doThrow(STORAGE_FAILURE).when(storageRpcMock).list(EMPTY_RPC_OPTIONS); + initializeService(); + try { + storage.list(); + fail(); + } catch (StorageException e) { + assertEquals(STORAGE_FAILURE.toString(), e.getMessage()); + } + } + + @Test + public void testListBlobs() { + String cursor = "cursor"; + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, EMPTY_RPC_OPTIONS); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = storage.list(BUCKET_NAME1); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsEmpty() { + doReturn( + Tuple.>of( + null, null)) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, EMPTY_RPC_OPTIONS); + + initializeService(); + Page page = storage.list(BUCKET_NAME1); + assertNull(page.getNextPageToken()); + assertArrayEquals( + ImmutableList.of().toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsWithOptions() { + String cursor = "cursor"; + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, BLOB_LIST_OPTIONS); + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsWithSelectedFields() { + String cursor = "cursor"; + ArgumentCaptor> capturedOptions = + ArgumentCaptor.forClass(Map.class); + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(Mockito.eq(BUCKET_NAME1), capturedOptions.capture()); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS); + assertEquals( + BLOB_LIST_PAGE_SIZE.getValue(), + capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.getRpcOption())); + assertEquals( + BLOB_LIST_PREFIX.getValue(), + capturedOptions.getValue().get(BLOB_LIST_PREFIX.getRpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.getRpcOption()); + assertTrue(selector.contains("prefixes")); + assertTrue(selector.contains("items(")); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("contentType")); + assertTrue(selector.contains("md5Hash")); + assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); + assertEquals(61, selector.length()); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsWithEmptyFields() { + String cursor = "cursor"; + ArgumentCaptor> capturedOptions = + ArgumentCaptor.forClass(Map.class); + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(Mockito.eq(BUCKET_NAME1), capturedOptions.capture()); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS); + assertEquals( + BLOB_LIST_PAGE_SIZE.getValue(), + capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.getRpcOption())); + assertEquals( + BLOB_LIST_PREFIX.getValue(), + capturedOptions.getValue().get(BLOB_LIST_PREFIX.getRpcOption())); + String selector = + (String) capturedOptions.getValue().get(BLOB_LIST_EMPTY_FIELDS.getRpcOption()); + assertTrue(selector.contains("prefixes")); + assertTrue(selector.contains("items(")); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); + assertEquals(41, selector.length()); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsCurrentDirectory() { + String cursor = "cursor"; + Map options = ImmutableMap.of(StorageRpc.Option.DELIMITER, "/"); + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, options); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = storage.list(BUCKET_NAME1, Storage.BlobListOption.currentDirectory()); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsDelimiter() { + String cursor = "cursor"; + String delimiter = "/"; + Map options = ImmutableMap.of(StorageRpc.Option.DELIMITER, delimiter); + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, options); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = storage.list(BUCKET_NAME1, Storage.BlobListOption.delimiter(delimiter)); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsWithOffset() { + String cursor = "cursor"; + String startOffset = "startOffset"; + String endOffset = "endOffset"; + Map options = + ImmutableMap.of( + StorageRpc.Option.START_OFF_SET, startOffset, StorageRpc.Option.END_OFF_SET, endOffset); + ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); + doReturn(result) + .doThrow(UNEXPECTED_CALL_EXCEPTION) + .when(storageRpcMock) + .list(BUCKET_NAME1, options); + + initializeService(); + ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); + Page page = + storage.list( + BUCKET_NAME1, + Storage.BlobListOption.startOffset(startOffset), + Storage.BlobListOption.endOffset(endOffset)); + assertEquals(cursor, page.getNextPageToken()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); + } + + @Test + public void testListBlobsWithException() { + doThrow(STORAGE_FAILURE).when(storageRpcMock).list(BUCKET_NAME1, EMPTY_RPC_OPTIONS); + initializeService(); + try { + storage.list(BUCKET_NAME1); + fail(); + } catch (StorageException e) { + assertEquals(STORAGE_FAILURE.toString(), e.getMessage()); + } + } + private void verifyChannelRead(ReadChannel channel, byte[] bytes) throws IOException { assertNotNull(channel); assertTrue(channel.isOpen()); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 1b78780f4..24f5ed9e7 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -30,7 +30,6 @@ import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.core.ApiClock; -import com.google.api.gax.paging.Page; import com.google.api.services.storage.model.Policy.Bindings; import com.google.api.services.storage.model.StorageObject; import com.google.api.services.storage.model.TestIamPermissionsResponse; @@ -38,7 +37,6 @@ import com.google.cloud.Identity; import com.google.cloud.Policy; import com.google.cloud.ServiceOptions; -import com.google.cloud.Tuple; import com.google.cloud.WriteChannel; import com.google.cloud.storage.Acl.Project; import com.google.cloud.storage.Acl.Project.ProjectRole; @@ -54,7 +52,6 @@ import com.google.cloud.storage.spi.v1.StorageRpc; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; import com.google.common.io.BaseEncoding; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; @@ -428,267 +425,6 @@ private void initializeServiceDependentObjects() { expectedBucket3 = new Bucket(storage, new BucketInfo.BuilderImpl(BUCKET_INFO3)); } - @Test - public void testListBuckets() { - String cursor = "cursor"; - ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(EMPTY_RPC_OPTIONS)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); - Page page = storage.list(); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); - } - - @Test - public void testListBucketsEmpty() { - EasyMock.expect(storageRpcMock.list(EMPTY_RPC_OPTIONS)) - .andReturn( - Tuple.>of(null, null)); - EasyMock.replay(storageRpcMock); - initializeService(); - Page page = storage.list(); - assertNull(page.getNextPageToken()); - assertArrayEquals( - ImmutableList.of().toArray(), Iterables.toArray(page.getValues(), Bucket.class)); - } - - @Test - public void testListBucketsWithOptions() { - String cursor = "cursor"; - ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_LIST_OPTIONS)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); - Page page = storage.list(BUCKET_LIST_PAGE_SIZE, BUCKET_LIST_PREFIX); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); - } - - @Test - public void testListBucketsWithSelectedFields() { - String cursor = "cursor"; - Capture> capturedOptions = Capture.newInstance(); - ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(EasyMock.capture(capturedOptions))).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); - Page page = storage.list(BUCKET_LIST_FIELDS); - String selector = (String) capturedOptions.getValue().get(BUCKET_LIST_FIELDS.getRpcOption()); - assertTrue(selector.contains("items(")); - assertTrue(selector.contains("name")); - assertTrue(selector.contains("acl")); - assertTrue(selector.contains("location")); - assertTrue(selector.contains("nextPageToken")); - assertTrue(selector.endsWith(")")); - assertEquals(38, selector.length()); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); - } - - @Test - public void testListBucketsWithEmptyFields() { - String cursor = "cursor"; - Capture> capturedOptions = Capture.newInstance(); - ImmutableList bucketInfoList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(bucketInfoList, BucketInfo.TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(EasyMock.capture(capturedOptions))).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); - Page page = storage.list(BUCKET_LIST_EMPTY_FIELDS); - String selector = - (String) capturedOptions.getValue().get(BUCKET_LIST_EMPTY_FIELDS.getRpcOption()); - assertTrue(selector.contains("items(")); - assertTrue(selector.contains("name")); - assertTrue(selector.contains("nextPageToken")); - assertTrue(selector.endsWith(")")); - assertEquals(25, selector.length()); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.getValues(), Bucket.class)); - } - - @Test - public void testListBlobs() { - String cursor = "cursor"; - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, EMPTY_RPC_OPTIONS)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = storage.list(BUCKET_NAME1); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsEmpty() { - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, EMPTY_RPC_OPTIONS)) - .andReturn( - Tuple.>of( - null, null)); - EasyMock.replay(storageRpcMock); - initializeService(); - Page page = storage.list(BUCKET_NAME1); - assertNull(page.getNextPageToken()); - assertArrayEquals( - ImmutableList.of().toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsWithOptions() { - String cursor = "cursor"; - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, BLOB_LIST_OPTIONS)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsWithSelectedFields() { - String cursor = "cursor"; - Capture> capturedOptions = Capture.newInstance(); - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect( - storageRpcMock.list(EasyMock.eq(BUCKET_NAME1), EasyMock.capture(capturedOptions))) - .andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS); - assertEquals( - BLOB_LIST_PAGE_SIZE.getValue(), - capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.getRpcOption())); - assertEquals( - BLOB_LIST_PREFIX.getValue(), - capturedOptions.getValue().get(BLOB_LIST_PREFIX.getRpcOption())); - String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.getRpcOption()); - assertTrue(selector.contains("prefixes")); - assertTrue(selector.contains("items(")); - assertTrue(selector.contains("bucket")); - assertTrue(selector.contains("name")); - assertTrue(selector.contains("contentType")); - assertTrue(selector.contains("md5Hash")); - assertTrue(selector.contains("nextPageToken")); - assertTrue(selector.endsWith(")")); - assertEquals(61, selector.length()); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsWithEmptyFields() { - String cursor = "cursor"; - Capture> capturedOptions = Capture.newInstance(); - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect( - storageRpcMock.list(EasyMock.eq(BUCKET_NAME1), EasyMock.capture(capturedOptions))) - .andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS); - assertEquals( - BLOB_LIST_PAGE_SIZE.getValue(), - capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.getRpcOption())); - assertEquals( - BLOB_LIST_PREFIX.getValue(), - capturedOptions.getValue().get(BLOB_LIST_PREFIX.getRpcOption())); - String selector = - (String) capturedOptions.getValue().get(BLOB_LIST_EMPTY_FIELDS.getRpcOption()); - assertTrue(selector.contains("prefixes")); - assertTrue(selector.contains("items(")); - assertTrue(selector.contains("bucket")); - assertTrue(selector.contains("name")); - assertTrue(selector.contains("nextPageToken")); - assertTrue(selector.endsWith(")")); - assertEquals(41, selector.length()); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsCurrentDirectory() { - String cursor = "cursor"; - Map options = ImmutableMap.of(StorageRpc.Option.DELIMITER, "/"); - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, options)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = storage.list(BUCKET_NAME1, Storage.BlobListOption.currentDirectory()); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsDelimiter() { - String cursor = "cursor"; - String delimiter = "/"; - Map options = ImmutableMap.of(StorageRpc.Option.DELIMITER, delimiter); - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, options)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = storage.list(BUCKET_NAME1, Storage.BlobListOption.delimiter(delimiter)); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - - @Test - public void testListBlobsWithOffset() { - String cursor = "cursor"; - String startOffset = "startOffset"; - String endOffset = "endOffset"; - Map options = - ImmutableMap.of( - StorageRpc.Option.START_OFF_SET, startOffset, StorageRpc.Option.END_OFF_SET, endOffset); - ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); - Tuple> result = - Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION)); - EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, options)).andReturn(result); - EasyMock.replay(storageRpcMock); - initializeService(); - ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); - Page page = - storage.list( - BUCKET_NAME1, - Storage.BlobListOption.startOffset(startOffset), - Storage.BlobListOption.endOffset(endOffset)); - assertEquals(cursor, page.getNextPageToken()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(page.getValues(), Blob.class)); - } - @Test public void testUpdateBucket() { BucketInfo updatedBucketInfo = BUCKET_INFO1.toBuilder().setIndexPage("some-page").build(); From edfd1e69e886adb04b98b54b3a63768c7e82b1e0 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 12 Aug 2020 23:35:50 +0200 Subject: [PATCH 21/35] deps: update dependency com.google.apis:google-api-services-storage to v1-rev20200727-1.30.10 (#457) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e5031c56..3672201db 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ com.google.apis google-api-services-storage - v1-rev20200611-1.30.10 + v1-rev20200727-1.30.10 org.easymock From c4b3f351b6882a535e17ef43ca52104d897448d9 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Thu, 13 Aug 2020 10:15:28 -0700 Subject: [PATCH 22/35] chore: update common templates (#459) * cleanup: removes unused kokoro config files * cleanup: removes unused kokoro config files Removes unused kokoro files from the java library template. We have stopped running some of these due to Github quota issues. * fix:reverts back samples.cfg files The files presubmit/samples.cfg and nightly/samples.cfg should remain in the java template repository. Co-authored-by: Jeffrey Rennie Source-Author: Thiago Nunes Source-Date: Thu Aug 6 09:48:58 2020 +1000 Source-Repo: googleapis/synthtool Source-Sha: 4530cc6ff080ef8aca258c1ec92c4db10a1bbfb4 Source-Link: https://github.com/googleapis/synthtool/commit/4530cc6ff080ef8aca258c1ec92c4db10a1bbfb4 * build: update dependencies check to only check for runtime and compile scopes * change:Updated dependencies check to only use runtime & compile scope * Update dependencies.sh * feat: update dependencies check to only check for runtime and compile scopes Co-authored-by: Saleh Mostafa Co-authored-by: Jeffrey Rennie Source-Author: salehsquared Source-Date: Thu Aug 6 13:01:02 2020 -0400 Source-Repo: googleapis/synthtool Source-Sha: f8823dec98277a9516f2fb6fae9f58b3a59a23e1 Source-Link: https://github.com/googleapis/synthtool/commit/f8823dec98277a9516f2fb6fae9f58b3a59a23e1 --- .kokoro/continuous/dependencies.cfg | 12 ------ .kokoro/continuous/integration.cfg | 7 ---- .kokoro/continuous/java11.cfg | 7 ---- .kokoro/continuous/java7.cfg | 7 ---- .kokoro/continuous/java8-osx.cfg | 3 -- .kokoro/continuous/java8-win.cfg | 3 -- .kokoro/continuous/lint.cfg | 13 ------- .kokoro/continuous/propose_release.cfg | 53 -------------------------- .kokoro/continuous/samples.cfg | 31 --------------- .kokoro/dependencies.sh | 9 +++-- .kokoro/nightly/dependencies.cfg | 12 ------ .kokoro/nightly/lint.cfg | 13 ------- synth.metadata | 16 +------- 13 files changed, 7 insertions(+), 179 deletions(-) delete mode 100644 .kokoro/continuous/dependencies.cfg delete mode 100644 .kokoro/continuous/integration.cfg delete mode 100644 .kokoro/continuous/java11.cfg delete mode 100644 .kokoro/continuous/java7.cfg delete mode 100644 .kokoro/continuous/java8-osx.cfg delete mode 100644 .kokoro/continuous/java8-win.cfg delete mode 100644 .kokoro/continuous/lint.cfg delete mode 100644 .kokoro/continuous/propose_release.cfg delete mode 100644 .kokoro/continuous/samples.cfg delete mode 100644 .kokoro/nightly/dependencies.cfg delete mode 100644 .kokoro/nightly/lint.cfg diff --git a/.kokoro/continuous/dependencies.cfg b/.kokoro/continuous/dependencies.cfg deleted file mode 100644 index a8b9fbf2d..000000000 --- a/.kokoro/continuous/dependencies.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-storage/.kokoro/dependencies.sh" -} diff --git a/.kokoro/continuous/integration.cfg b/.kokoro/continuous/integration.cfg deleted file mode 100644 index 3b017fc80..000000000 --- a/.kokoro/continuous/integration.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} diff --git a/.kokoro/continuous/java11.cfg b/.kokoro/continuous/java11.cfg deleted file mode 100644 index 709f2b4c7..000000000 --- a/.kokoro/continuous/java11.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java11" -} diff --git a/.kokoro/continuous/java7.cfg b/.kokoro/continuous/java7.cfg deleted file mode 100644 index cb24f44ee..000000000 --- a/.kokoro/continuous/java7.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java7" -} diff --git a/.kokoro/continuous/java8-osx.cfg b/.kokoro/continuous/java8-osx.cfg deleted file mode 100644 index 79fedbfd8..000000000 --- a/.kokoro/continuous/java8-osx.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -build_file: "java-storage/.kokoro/build.sh" diff --git a/.kokoro/continuous/java8-win.cfg b/.kokoro/continuous/java8-win.cfg deleted file mode 100644 index 6ae002330..000000000 --- a/.kokoro/continuous/java8-win.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -build_file: "java-storage/.kokoro/build.bat" diff --git a/.kokoro/continuous/lint.cfg b/.kokoro/continuous/lint.cfg deleted file mode 100644 index 6d323c8ae..000000000 --- a/.kokoro/continuous/lint.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. - -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "lint" -} \ No newline at end of file diff --git a/.kokoro/continuous/propose_release.cfg b/.kokoro/continuous/propose_release.cfg deleted file mode 100644 index b115c5eb8..000000000 --- a/.kokoro/continuous/propose_release.cfg +++ /dev/null @@ -1,53 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "java-storage/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:10-user" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-storage/.kokoro/continuous/propose_release.sh" -} - -# tokens used by release-please to keep an up-to-date release PR. -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-key-release-please" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-token-release-please" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "github-magic-proxy-url-release-please" - } - } -} diff --git a/.kokoro/continuous/samples.cfg b/.kokoro/continuous/samples.cfg deleted file mode 100644 index fa7b493d0..000000000 --- a/.kokoro/continuous/samples.cfg +++ /dev/null @@ -1,31 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "samples" -} - -env_vars: { - key: "GCLOUD_PROJECT" - value: "gcloud-devel" -} - -env_vars: { - key: "GOOGLE_APPLICATION_CREDENTIALS" - value: "keystore/73713_java_it_service_account" -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "java_it_service_account" - } - } -} diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh index cee4f11e7..c91e5a569 100755 --- a/.kokoro/dependencies.sh +++ b/.kokoro/dependencies.sh @@ -43,12 +43,13 @@ function completenessCheck() { # Output dep list with compile scope generated using the original pom # Running mvn dependency:list on Java versions that support modules will also include the module of the dependency. # This is stripped from the output as it is not present in the flattened pom. + # Only dependencies with 'compile' or 'runtime' scope are included from original dependency list. msg "Generating dependency list using original pom..." - mvn dependency:list -f pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// | grep -v ':test$' >.org-list.txt + mvn dependency:list -f pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// >.org-list.txt - # Output dep list generated using the flattened pom (test scope deps are ommitted) + # Output dep list generated using the flattened pom (only 'compile' and 'runtime' scopes) msg "Generating dependency list using flattened pom..." - mvn dependency:list -f .flattened-pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt + mvn dependency:list -f .flattened-pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt # Compare two dependency lists msg "Comparing dependency lists..." @@ -85,4 +86,4 @@ then else msg "Errors found. See log statements above." exit 1 -fi +fi \ No newline at end of file diff --git a/.kokoro/nightly/dependencies.cfg b/.kokoro/nightly/dependencies.cfg deleted file mode 100644 index a8b9fbf2d..000000000 --- a/.kokoro/nightly/dependencies.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/java-storage/.kokoro/dependencies.sh" -} diff --git a/.kokoro/nightly/lint.cfg b/.kokoro/nightly/lint.cfg deleted file mode 100644 index 6d323c8ae..000000000 --- a/.kokoro/nightly/lint.cfg +++ /dev/null @@ -1,13 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Configure the docker image for kokoro-trampoline. - -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -env_vars: { - key: "JOB_TYPE" - value: "lint" -} \ No newline at end of file diff --git a/synth.metadata b/synth.metadata index da1556f25..9564ab09a 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-storage.git", - "sha": "0ce82ccf1e6ed8f46aeda92fe5f17eb427c3a7d9" + "sha": "f8d6e158a06aec926fb7bc42f10483d56696a37e" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "dd230c816f88d0141fcd0be83498986287220d1b" + "sha": "f8823dec98277a9516f2fb6fae9f58b3a59a23e1" } } ], @@ -30,27 +30,16 @@ ".kokoro/common.cfg", ".kokoro/common.sh", ".kokoro/continuous/common.cfg", - ".kokoro/continuous/dependencies.cfg", - ".kokoro/continuous/integration.cfg", - ".kokoro/continuous/java11.cfg", - ".kokoro/continuous/java7.cfg", - ".kokoro/continuous/java8-osx.cfg", - ".kokoro/continuous/java8-win.cfg", ".kokoro/continuous/java8.cfg", - ".kokoro/continuous/lint.cfg", - ".kokoro/continuous/propose_release.cfg", - ".kokoro/continuous/samples.cfg", ".kokoro/dependencies.sh", ".kokoro/linkage-monitor.sh", ".kokoro/nightly/common.cfg", - ".kokoro/nightly/dependencies.cfg", ".kokoro/nightly/integration.cfg", ".kokoro/nightly/java11.cfg", ".kokoro/nightly/java7.cfg", ".kokoro/nightly/java8-osx.cfg", ".kokoro/nightly/java8-win.cfg", ".kokoro/nightly/java8.cfg", - ".kokoro/nightly/lint.cfg", ".kokoro/nightly/samples.cfg", ".kokoro/populate-secrets.sh", ".kokoro/presubmit/clirr.cfg", @@ -79,7 +68,6 @@ ".kokoro/release/stage.sh", ".kokoro/trampoline.sh", "CODE_OF_CONDUCT.md", - "CONTRIBUTING.md", "LICENSE", "README.md", "codecov.yaml", From 949c140366cafd85c0ab97bbf86981963fdd4846 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 14 Aug 2020 04:26:08 +0200 Subject: [PATCH 23/35] chore(deps): update dependency com.google.cloud:libraries-bom to v9 (#465) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [com.google.cloud:libraries-bom](https://togithub.com/GoogleCloudPlatform/cloud-opensource-java) | major | `8.1.0` -> `9.0.0` | --- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#googleapis/java-storage). --- samples/snippets/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index e1a723314..676b3054c 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -30,7 +30,7 @@ com.google.cloud libraries-bom - 8.1.0 + 9.0.0 pom import From 0d30ef008cdc82f434b09fe8105924c0aa32d9d1 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Fri, 14 Aug 2020 07:59:59 -0700 Subject: [PATCH 24/35] chore: regenerate README autosynth cannot find the source of changes triggered by earlier changes in this repository, or by version upgrades to tools such as linters. --- README.md | 2 +- synth.metadata | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 000f72b38..27e21ac55 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 com.google.cloud libraries-bom - 8.1.0 + 9.0.0 pom import diff --git a/synth.metadata b/synth.metadata index 9564ab09a..d1490ecf4 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-storage.git", - "sha": "f8d6e158a06aec926fb7bc42f10483d56696a37e" + "sha": "949c140366cafd85c0ab97bbf86981963fdd4846" } }, { From cf3060a0dad5b559cbb8fce72c4a1bbb250cbb70 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 18 Aug 2020 00:04:13 +0200 Subject: [PATCH 25/35] chore(deps): update dependency com.google.cloud:libraries-bom to v9.1.0 --- samples/snippets/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 676b3054c..a2e44bb9c 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -30,7 +30,7 @@ com.google.cloud libraries-bom - 9.0.0 + 9.1.0 pom import From 6c372fa81e49ac74bdda6f9b10914fac42767247 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 19 Aug 2020 02:14:35 +0200 Subject: [PATCH 26/35] deps: update dependency com.google.api.grpc:proto-google-cloud-kms-v1 to v0.86.1 (#464) --- google-cloud-storage/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index ffacd7054..587822656 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -123,7 +123,7 @@ com.google.api.grpc proto-google-cloud-kms-v1 - 0.86.0 + 0.86.1 test From e011ec15c61ad6e96e579556c3317012475462f9 Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Thu, 20 Aug 2020 15:40:14 -0700 Subject: [PATCH 27/35] samples: add presubmit lint check (#473) This PR was generated using Autosynth. :rainbow: Synth log will be available here: https://source.cloud.google.com/results/invocations/04a3c534-5129-4a05-922f-148232720a95/targets - [ ] To automatically regenerate this PR, check this box. Source-Link: https://github.com/googleapis/synthtool/commit/9602086c6c5b05db77950c7f7495a2a3868f3537 Source-Link: https://github.com/googleapis/synthtool/commit/c3caf0704f25a0c365f1c315e804a30b87c62a75 --- .github/workflows/samples.yaml | 14 ++++++++++++++ README.md | 2 +- synth.metadata | 5 +++-- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/samples.yaml diff --git a/.github/workflows/samples.yaml b/.github/workflows/samples.yaml new file mode 100644 index 000000000..a1d500730 --- /dev/null +++ b/.github/workflows/samples.yaml @@ -0,0 +1,14 @@ +on: + pull_request: +name: samples +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 8 + - name: Run checkstyle + run: mvn -P lint --quiet --batch-mode checkstyle:check + working-directory: samples/snippets diff --git a/README.md b/README.md index 27e21ac55..349cb1689 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 com.google.cloud libraries-bom - 9.0.0 + 9.1.0 pom import diff --git a/synth.metadata b/synth.metadata index d1490ecf4..31f1a9816 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-storage.git", - "sha": "949c140366cafd85c0ab97bbf86981963fdd4846" + "sha": "6c372fa81e49ac74bdda6f9b10914fac42767247" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "f8823dec98277a9516f2fb6fae9f58b3a59a23e1" + "sha": "9602086c6c5b05db77950c7f7495a2a3868f3537" } } ], @@ -24,6 +24,7 @@ ".github/release-please.yml", ".github/trusted-contribution.yml", ".github/workflows/ci.yaml", + ".github/workflows/samples.yaml", ".kokoro/build.bat", ".kokoro/build.sh", ".kokoro/coerce_logs.sh", From cdb3787d2d21efc567c1b5a966c9354f0a65bcff Mon Sep 17 00:00:00 2001 From: Dmitry <58846611+dmitry-fa@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:54:46 +0300 Subject: [PATCH 28/35] chore: tiny correction (#471) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39d1c88d9..ccbd5094f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ integration tests. ```bash export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json -export IT_SERVICE_ACCOUNT_EMAIL=my-service-account@my-project.gserviceaccount.com +export IT_SERVICE_ACCOUNT_EMAIL=my-service-account@my-project.iam.gserviceaccount.com mvn -Penable-integration-tests clean verify ``` From 8602b81eae95868e184fd4ab290396707bd21a8e Mon Sep 17 00:00:00 2001 From: Dmitry <58846611+dmitry-fa@users.noreply.github.com> Date: Fri, 21 Aug 2020 20:21:29 +0300 Subject: [PATCH 29/35] fix: PostPolicyV4 classes could be improved (#442) * fix: PostPolicyV4 classes could be improved * fix: javadoc * fix: javadoc * fix: javadoc * test: PostPolicyV4Test unit tests * test: PostPolicyV4Test unit tests * test: PostPolicyV4Test unit tests * test: PostPolicyV4Test unit tests * fix: better URL syntax check --- .../google/cloud/storage/PostPolicyV4.java | 192 +++++- .../com/google/cloud/storage/Storage.java | 18 +- .../cloud/storage/PostPolicyV4Test.java | 592 ++++++++++++++++++ 3 files changed, 765 insertions(+), 37 deletions(-) create mode 100644 google-cloud-storage/src/test/java/com/google/cloud/storage/PostPolicyV4Test.java diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java index cc9b413a2..96afca06e 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/PostPolicyV4.java @@ -18,56 +18,125 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import java.net.URI; +import java.net.URISyntaxException; import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.TimeUnit; /** - * Presigned V4 post policy. + * Presigned V4 post policy. Instances of {@code PostPolicyV4} include a URL and a map of fields + * that can be specified in an HTML form to submit a POST request to upload an object. * - * @see POST Object + *

See POST Object for + * details of upload by using HTML forms. + * + *

See {@link Storage#generateSignedPostPolicyV4(BlobInfo, long, TimeUnit, + * PostPolicyV4.PostFieldsV4, PostPolicyV4.PostConditionsV4, Storage.PostPolicyV4Option...)} for + * example of usage. */ public final class PostPolicyV4 { private final String url; private final Map fields; private PostPolicyV4(String url, Map fields) { + try { + if (!new URI(url).isAbsolute()) { + throw new IllegalArgumentException(url + " is not an absolute URL"); + } + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + PostFieldsV4.validateFields(fields); + this.url = url; - this.fields = fields; + this.fields = Collections.unmodifiableMap(fields); } + /** + * Constructs {@code PostPolicyV4} instance of the given URL and fields map. + * + * @param url URL for the HTTP POST request + * @param fields HTML form fields + * @return constructed object + * @throws IllegalArgumentException if URL is malformed or fields are not valid + */ public static PostPolicyV4 of(String url, Map fields) { return new PostPolicyV4(url, fields); } + /** Returns the URL for the HTTP POST request */ public String getUrl() { return url; } + /** Returns the HTML form fields */ public Map getFields() { return fields; } /** - * Class representing which fields to specify in a V4 POST request. + * A helper class to define fields to be specified in a V4 POST request. Instance of this class + * helps to construct {@code PostPolicyV4} objects. Used in: {@link + * Storage#generateSignedPostPolicyV4(BlobInfo, long, TimeUnit, PostPolicyV4.PostFieldsV4, + * PostPolicyV4.PostConditionsV4, Storage.PostPolicyV4Option...)}. * * @see POST * Object Form fields */ public static final class PostFieldsV4 { private final Map fieldsMap; + private static final List VALID_FIELDS = + Arrays.asList( + "acl", + "bucket", + "cache-control", + "content-disposition", + "content-encoding", + "content-type", + "expires", + "file", + "key", + "policy", + "success_action_redirect", + "success_action_status", + "x-goog-algorithm", + "x-goog-credential", + "x-goog-date", + "x-goog-signature"); + + private static void validateFields(Map fields) { + for (String key : fields.keySet()) { + if (!VALID_FIELDS.contains(key.toLowerCase()) + && !key.startsWith(Builder.CUSTOM_FIELD_PREFIX)) { + throw new IllegalArgumentException("Invalid key: " + key); + } + } + } private PostFieldsV4(Builder builder) { - this.fieldsMap = builder.fieldsMap; + this(builder.fieldsMap); } private PostFieldsV4(Map fields) { - this.fieldsMap = fields; + validateFields(fields); + this.fieldsMap = Collections.unmodifiableMap(fields); } + /** + * Constructs {@code PostPolicyV4.PostFieldsV4} object of the given field map. + * + * @param fields a map of the HTML form fields + * @return constructed object + * @throws IllegalArgumentException if an unsupported field is specified + */ public static PostFieldsV4 of(Map fields) { return new PostFieldsV4(fields); } @@ -112,8 +181,14 @@ public Builder setContentEncoding(String contentEncoding) { return this; } + /** + * @deprecated Invocation of this method has no effect, because all valid HTML form fields + * except Content-Length can use exact matching. Use {@link + * PostPolicyV4.PostConditionsV4.Builder#addContentLengthRangeCondition(int, int)} to + * specify a range for the content-length. + */ + @Deprecated public Builder setContentLength(int contentLength) { - fieldsMap.put("content-length", "" + contentLength); return this; } @@ -122,7 +197,7 @@ public Builder setContentType(String contentType) { return this; } - /** @deprecated use {@link #setExpires(String)} */ + /** @deprecated Use {@link #setExpires(String)}. */ @Deprecated public Builder Expires(String expires) { return setExpires(expires); @@ -143,7 +218,7 @@ public Builder setSuccessActionStatus(int successActionStatus) { return this; } - /** @deprecated use {@link #setCustomMetadataField(String, String)} */ + /** @deprecated Use {@link #setCustomMetadataField(String, String)}. */ @Deprecated public Builder AddCustomMetadataField(String field, String value) { return setCustomMetadataField(field, value); @@ -151,7 +226,7 @@ public Builder AddCustomMetadataField(String field, String value) { public Builder setCustomMetadataField(String field, String value) { if (!field.startsWith(CUSTOM_FIELD_PREFIX)) { - field = CUSTOM_FIELD_PREFIX + value; + field = CUSTOM_FIELD_PREFIX + field; } fieldsMap.put(field, value); return this; @@ -160,7 +235,9 @@ public Builder setCustomMetadataField(String field, String value) { } /** - * Class for specifying conditions in a V4 POST Policy document. + * A helper class for specifying conditions in a V4 POST Policy document. Used in: {@link + * Storage#generateSignedPostPolicyV4(BlobInfo, long, TimeUnit, PostPolicyV4.PostFieldsV4, + * PostPolicyV4.PostConditionsV4, Storage.PostPolicyV4Option...)}. * * @see * Policy document @@ -183,14 +260,14 @@ public static Builder newBuilder() { } public Set getConditions() { - return conditions; + return Collections.unmodifiableSet(conditions); } public static class Builder { - Set conditions; + private final Set conditions; private Builder() { - this.conditions = new LinkedHashSet<>(); + this(new LinkedHashSet()); } private Builder(Set conditions) { @@ -206,64 +283,93 @@ public PostConditionsV4 build() { } public Builder addAclCondition(ConditionV4Type type, String acl) { + checkType(type, "acl"); conditions.add(new ConditionV4(type, "acl", acl)); return this; } public Builder addBucketCondition(ConditionV4Type type, String bucket) { + checkType(type, "bucket"); conditions.add(new ConditionV4(type, "bucket", bucket)); return this; } public Builder addCacheControlCondition(ConditionV4Type type, String cacheControl) { + checkType(type, "cache-control"); conditions.add(new ConditionV4(type, "cache-control", cacheControl)); return this; } public Builder addContentDispositionCondition( ConditionV4Type type, String contentDisposition) { + checkType(type, "content-disposition"); conditions.add(new ConditionV4(type, "content-disposition", contentDisposition)); return this; } public Builder addContentEncodingCondition(ConditionV4Type type, String contentEncoding) { + checkType(type, "content-encoding"); conditions.add(new ConditionV4(type, "content-encoding", contentEncoding)); return this; } + /** + * @deprecated Invocation of this method has no effect. Use {@link + * #addContentLengthRangeCondition(int, int)} to specify a range for the content-length. + */ public Builder addContentLengthCondition(ConditionV4Type type, int contentLength) { - conditions.add(new ConditionV4(type, "content-length", "" + contentLength)); return this; } public Builder addContentTypeCondition(ConditionV4Type type, String contentType) { + checkType(type, "content-type"); conditions.add(new ConditionV4(type, "content-type", contentType)); return this; } + /** @deprecated Use {@link #addExpiresCondition(long)} */ + @Deprecated public Builder addExpiresCondition(ConditionV4Type type, long expires) { - conditions.add(new ConditionV4(type, "expires", dateFormat.format(expires))); - return this; + return addExpiresCondition(expires); } + /** @deprecated Use {@link #addExpiresCondition(String)} */ + @Deprecated public Builder addExpiresCondition(ConditionV4Type type, String expires) { - conditions.add(new ConditionV4(type, "expires", expires)); + return addExpiresCondition(expires); + } + + public Builder addExpiresCondition(long expires) { + return addExpiresCondition(dateFormat.format(expires)); + } + + public Builder addExpiresCondition(String expires) { + conditions.add(new ConditionV4(ConditionV4Type.MATCHES, "expires", expires)); return this; } public Builder addKeyCondition(ConditionV4Type type, String key) { + checkType(type, "key"); conditions.add(new ConditionV4(type, "key", key)); return this; } public Builder addSuccessActionRedirectUrlCondition( ConditionV4Type type, String successActionRedirectUrl) { + checkType(type, "success_action_redirect"); conditions.add(new ConditionV4(type, "success_action_redirect", successActionRedirectUrl)); return this; } + /** @deprecated Use {@link #addSuccessActionStatusCondition(int)} */ + @Deprecated public Builder addSuccessActionStatusCondition(ConditionV4Type type, int status) { - conditions.add(new ConditionV4(type, "success_action_status", "" + status)); + return addSuccessActionStatusCondition(status); + } + + public Builder addSuccessActionStatusCondition(int status) { + conditions.add( + new ConditionV4(ConditionV4Type.MATCHES, "success_action_status", "" + status)); return this; } @@ -276,11 +382,17 @@ Builder addCustomCondition(ConditionV4Type type, String field, String value) { conditions.add(new ConditionV4(type, field, value)); return this; } + + private void checkType(ConditionV4Type type, String field) { + if (type != ConditionV4Type.MATCHES && type != ConditionV4Type.STARTS_WITH) { + throw new IllegalArgumentException("Field " + field + " can't use " + type); + } + } } } /** - * Class for a V4 POST Policy document. + * Class for a V4 POST Policy document. Used by Storage to construct {@code PostPolicyV4} objects. * * @see * Policy document @@ -367,9 +479,20 @@ public String toJson() { } public enum ConditionV4Type { - MATCHES, - STARTS_WITH, - CONTENT_LENGTH_RANGE + MATCHES("eq"), + STARTS_WITH("starts-with"), + CONTENT_LENGTH_RANGE("content-length-range"); + + private final String name; + + ConditionV4Type(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } } /** @@ -378,12 +501,12 @@ public enum ConditionV4Type { * @see * Policy document */ - static final class ConditionV4 { - final ConditionV4Type type; - final String operand1; - final String operand2; + public static final class ConditionV4 { + public final ConditionV4Type type; + public final String operand1; + public final String operand2; - private ConditionV4(ConditionV4Type type, String operand1, String operand2) { + ConditionV4(ConditionV4Type type, String operand1, String operand2) { this.type = type; this.operand1 = operand1; this.operand2 = operand2; @@ -401,5 +524,18 @@ public boolean equals(Object other) { public int hashCode() { return Objects.hash(type, operand1, operand2); } + + /** + * Examples of returned strings: {@code ["eq", "$key", "test-object"]}, {@code ["starts-with", + * "$acl", "public"]}, {@code ["content-length-range", 246, 266]}. + */ + @Override + public String toString() { + String body = + type == ConditionV4Type.CONTENT_LENGTH_RANGE + ? operand1 + ", " + operand2 + : "\"$" + operand1 + "\", \"" + operand2 + "\""; + return "[\"" + type + "\", " + body + "]"; + } } } 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 82ba17508..6b8fc4990 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 @@ -2843,6 +2843,8 @@ Blob createFrom( * }

* * @param blobInfo the blob uploaded in the form + * @param duration time before expiration + * @param unit duration time unit * @param fields the fields specified in the form * @param conditions which conditions every upload must satisfy * @param duration how long until the form expires, in milliseconds @@ -2861,9 +2863,8 @@ PostPolicyV4 generateSignedPostPolicyV4( /** * Generates a presigned post policy without any conditions. Automatically creates required - * conditions. See full documentation for generateSignedPostPolicyV4( BlobInfo blobInfo, long - * duration, TimeUnit unit, PostFieldsV4 fields, PostConditionsV4 conditions, - * PostPolicyV4Option... options) above. + * conditions. See full documentation for {@link #generateSignedPostPolicyV4(BlobInfo, long, + * TimeUnit, PostPolicyV4.PostFieldsV4, PostPolicyV4.PostConditionsV4, PostPolicyV4Option...)}. */ PostPolicyV4 generateSignedPostPolicyV4( BlobInfo blobInfo, @@ -2874,9 +2875,8 @@ PostPolicyV4 generateSignedPostPolicyV4( /** * Generates a presigned post policy without any fields. Automatically creates required fields. - * See full documentation for generateSignedPostPolicyV4( BlobInfo blobInfo, long duration, - * TimeUnit unit, PostFieldsV4 fields, PostConditionsV4 conditions, PostPolicyV4Option... options) - * above. + * See full documentation for {@link #generateSignedPostPolicyV4(BlobInfo, long, TimeUnit, + * PostPolicyV4.PostFieldsV4, PostPolicyV4.PostConditionsV4, PostPolicyV4Option...)}. */ PostPolicyV4 generateSignedPostPolicyV4( BlobInfo blobInfo, @@ -2887,9 +2887,9 @@ PostPolicyV4 generateSignedPostPolicyV4( /** * Generates a presigned post policy without any fields or conditions. Automatically creates - * required fields and conditions. See full documentation for generateSignedPostPolicyV4( BlobInfo - * blobInfo, long duration, TimeUnit unit, PostFieldsV4 fields, PostConditionsV4 conditions, - * PostPolicyV4Option... options) above. + * required fields and conditions. See full documentation for {@link + * #generateSignedPostPolicyV4(BlobInfo, long, TimeUnit, PostPolicyV4.PostFieldsV4, + * PostPolicyV4.PostConditionsV4, PostPolicyV4Option...)}. */ PostPolicyV4 generateSignedPostPolicyV4( BlobInfo blobInfo, long duration, TimeUnit unit, PostPolicyV4Option... options); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/PostPolicyV4Test.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/PostPolicyV4Test.java new file mode 100644 index 000000000..8213f6093 --- /dev/null +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/PostPolicyV4Test.java @@ -0,0 +1,592 @@ +/* + * Copyright 2020 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import org.junit.Test; + +public class PostPolicyV4Test { + private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + + private void assertNotSameButEqual(Map expected, Map returned) { + assertNotSame(expected, returned); + assertEquals("map sizes", expected.size(), returned.size()); + for (String key : expected.keySet()) { + assertEquals("value of $" + key, expected.get(key), returned.get(key)); + } + } + + private static final String[] VALID_FIELDS = { + "acl", + "bucket", + "cache-control", + "content-disposition", + "content-encoding", + "content-type", + "expires", + "file", + "key", + "policy", + "success_action_redirect", + "success_action_status", + "x-goog-algorithm", + "x-goog-credential", + "x-goog-date", + "x-goog-signature", + }; + + private static final String CUSTOM_PREFIX = "x-goog-meta-"; + + private static Map initAllFields() { + Map fields = new HashMap<>(); + for (String key : VALID_FIELDS) { + fields.put(key, "value of " + key); + } + fields.put(CUSTOM_PREFIX + "custom", "value of custom field"); + return Collections.unmodifiableMap(fields); + } + + private static final Map ALL_FIELDS = initAllFields(); + + @Test + public void testPostPolicyV4_of() { + String url = "http://example.com"; + PostPolicyV4 policy = PostPolicyV4.of(url, ALL_FIELDS); + assertEquals(url, policy.getUrl()); + assertNotSameButEqual(ALL_FIELDS, policy.getFields()); + } + + @Test + public void testPostPolicyV4_ofMalformedURL() { + try { + PostPolicyV4.of("example.com", new HashMap()); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("example.com is not an absolute URL", e.getMessage()); + } + + try { + PostPolicyV4.of("Scio nescio", new HashMap()); + fail(); + } catch (IllegalArgumentException e) { + assertEquals( + "java.net.URISyntaxException: Illegal character in path at index 4: Scio nescio", + e.getMessage()); + } + } + + @Test + public void testPostPolicyV4_ofInvalidField() { + Map fields = new HashMap<>(ALL_FIELDS); + fields.put("$file", "file.txt"); + try { + PostPolicyV4.of("http://google.com", fields); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Invalid key: $file", e.getMessage()); + } + } + + @Test + public void testPostFieldsV4_of() { + PostPolicyV4.PostFieldsV4 fields = PostPolicyV4.PostFieldsV4.of(ALL_FIELDS); + assertNotSameButEqual(ALL_FIELDS, fields.getFieldsMap()); + } + + @Test + public void testPostFieldsV4_ofInvalidField() { + Map map = new HashMap<>(); + map.put("$file", "file.txt"); + try { + PostPolicyV4.PostFieldsV4.of(map); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Invalid key: $file", e.getMessage()); + } + } + + @Test + public void testPostPolicyV4_builder() { + PostPolicyV4.PostFieldsV4.Builder builder = PostPolicyV4.PostFieldsV4.newBuilder(); + builder.setAcl("acl"); + builder.setCacheControl("cache-control"); + builder.setContentDisposition("content-disposition"); + builder.setContentType("content-type"); + builder.setExpires("expires"); + builder.setSuccessActionRedirect("success_action_redirect"); + Map map = builder.build().getFieldsMap(); + assertEquals("map size", 6, map.size()); + for (String key : map.keySet()) { + assertEquals("value of $" + key, key, map.get(key)); + } + + Map expectedUpdated = new HashMap<>(map); + builder.setCustomMetadataField("xxx", "XXX"); + builder.setCustomMetadataField(CUSTOM_PREFIX + "yyy", "YYY"); + builder.setAcl(null); + builder.setContentType("new-content-type"); + builder.setSuccessActionStatus(42); + expectedUpdated.put(CUSTOM_PREFIX + "xxx", "XXX"); + expectedUpdated.put(CUSTOM_PREFIX + "yyy", "YYY"); + expectedUpdated.put("acl", null); + expectedUpdated.put("content-type", "new-content-type"); + expectedUpdated.put("success_action_status", "42"); + Map updated = builder.build().getFieldsMap(); + assertNotSameButEqual(expectedUpdated, updated); + } + + @Test + public void testPostPolicyV4_setContentLength() { + PostPolicyV4.PostFieldsV4.Builder builder = PostPolicyV4.PostFieldsV4.newBuilder(); + builder.setContentLength(12345); + assertTrue(builder.build().getFieldsMap().isEmpty()); + } + + @Test + public void testPostConditionsV4_builder() { + PostPolicyV4.PostConditionsV4.Builder builder = PostPolicyV4.PostConditionsV4.newBuilder(); + assertTrue(builder.build().getConditions().isEmpty()); + + builder.addAclCondition(PostPolicyV4.ConditionV4Type.STARTS_WITH, "public"); + builder.addBucketCondition(PostPolicyV4.ConditionV4Type.MATCHES, "travel-maps"); + builder.addContentLengthRangeCondition(0, 100000); + + PostPolicyV4.PostConditionsV4 postConditionsV4 = builder.build(); + Set conditions = postConditionsV4.getConditions(); + assertEquals(3, conditions.size()); + + try { + conditions.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + PostPolicyV4.PostConditionsV4 postConditionsV4Extended = + postConditionsV4 + .toBuilder() + .addCustomCondition(PostPolicyV4.ConditionV4Type.STARTS_WITH, "key", "") + .build(); + assertEquals(4, postConditionsV4Extended.getConditions().size()); + } + + interface ConditionTest { + /** + * Calls one of addCondition method on the given builder and returns expected ConditionV4 + * object. + */ + PostPolicyV4.ConditionV4 addCondition(PostPolicyV4.PostConditionsV4.Builder builder); + } + + @Test + public void testPostConditionsV4_addCondition() { + // shortcuts + final PostPolicyV4.ConditionV4Type eq = PostPolicyV4.ConditionV4Type.MATCHES; + final PostPolicyV4.ConditionV4Type startsWith = PostPolicyV4.ConditionV4Type.STARTS_WITH; + final PostPolicyV4.ConditionV4Type range = PostPolicyV4.ConditionV4Type.CONTENT_LENGTH_RANGE; + + ConditionTest[] cases = { + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addContentLengthRangeCondition(123, 456); + return new PostPolicyV4.ConditionV4(range, "123", "456"); + } + + @Override + public String toString() { + return "addContentLengthRangeCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + long date = 2000000000000L; + builder.addExpiresCondition(date); + return new PostPolicyV4.ConditionV4(eq, "expires", dateFormat.format(date)); + } + + @Override + public String toString() { + return "addExpiresCondition(long)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addExpiresCondition("2030-Dec-31"); + return new PostPolicyV4.ConditionV4(eq, "expires", "2030-Dec-31"); + } + + @Override + public String toString() { + return "addExpiresCondition(String)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addExpiresCondition(range, 0); + return new PostPolicyV4.ConditionV4(eq, "expires", dateFormat.format(0)); + } + + @Override + public String toString() { + return "@deprecated addExpiresCondition(type,long)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addExpiresCondition(startsWith, "2030-Dec-31"); + return new PostPolicyV4.ConditionV4(eq, "expires", "2030-Dec-31"); + } + + @Override + public String toString() { + return "@deprecated addExpiresCondition(type,String)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addSuccessActionStatusCondition(202); + return new PostPolicyV4.ConditionV4(eq, "success_action_status", "202"); + } + + @Override + public String toString() { + return "addSuccessActionStatusCondition(int)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addSuccessActionStatusCondition(startsWith, 202); + return new PostPolicyV4.ConditionV4(eq, "success_action_status", "202"); + } + + @Override + public String toString() { + return "@deprecated addSuccessActionStatusCondition(type,int)"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addAclCondition(startsWith, "read"); + return new PostPolicyV4.ConditionV4(startsWith, "acl", "read"); + } + + @Override + public String toString() { + return "addAclCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addBucketCondition(eq, "my-bucket"); + return new PostPolicyV4.ConditionV4(eq, "bucket", "my-bucket"); + } + + @Override + public String toString() { + return "addBucketCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addCacheControlCondition(eq, "false"); + return new PostPolicyV4.ConditionV4(eq, "cache-control", "false"); + } + + @Override + public String toString() { + return "addCacheControlCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addContentDispositionCondition(startsWith, "gzip"); + return new PostPolicyV4.ConditionV4(startsWith, "content-disposition", "gzip"); + } + + @Override + public String toString() { + return "addContentDispositionCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addContentEncodingCondition(eq, "koi8"); + return new PostPolicyV4.ConditionV4(eq, "content-encoding", "koi8"); + } + + @Override + public String toString() { + return "addContentEncodingCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addContentTypeCondition(startsWith, "application/"); + return new PostPolicyV4.ConditionV4(startsWith, "content-type", "application/"); + } + + @Override + public String toString() { + return "addContentTypeCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addKeyCondition(startsWith, ""); + return new PostPolicyV4.ConditionV4(startsWith, "key", ""); + } + + @Override + public String toString() { + return "addKeyCondition()"; + } + }, + new ConditionTest() { + @Override + public PostPolicyV4.ConditionV4 addCondition( + PostPolicyV4.PostConditionsV4.Builder builder) { + builder.addSuccessActionRedirectUrlCondition(eq, "fail"); + return new PostPolicyV4.ConditionV4(eq, "success_action_redirect", "fail"); + } + + @Override + public String toString() { + return "addSuccessActionRedirectUrlCondition()"; + } + }, + }; + + for (ConditionTest testCase : cases) { + PostPolicyV4.PostConditionsV4.Builder builder = PostPolicyV4.PostConditionsV4.newBuilder(); + PostPolicyV4.ConditionV4 expected = testCase.addCondition(builder); + Set conditions = builder.build().getConditions(); + assertEquals("size", 1, conditions.size()); + PostPolicyV4.ConditionV4 actual = conditions.toArray(new PostPolicyV4.ConditionV4[1])[0]; + assertEquals(testCase.toString(), expected, actual); + } + } + + @Test + public void testPostConditionsV4_addConditionFail() { + final PostPolicyV4.PostConditionsV4.Builder builder = + PostPolicyV4.PostConditionsV4.newBuilder(); + final PostPolicyV4.ConditionV4Type range = PostPolicyV4.ConditionV4Type.CONTENT_LENGTH_RANGE; + + Callable[] cases = { + new Callable() { + @Override + public Void call() { + builder.addAclCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "acl"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addBucketCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "bucket"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addCacheControlCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "cache-control"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addContentDispositionCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "content-disposition"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addContentEncodingCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "content-encoding"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addContentTypeCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "content-type"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addKeyCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "key"; + } + }, + new Callable() { + @Override + public Void call() { + builder.addSuccessActionRedirectUrlCondition(range, ""); + return null; + } + + @Override + public String toString() { + return "success_action_redirect"; + } + }, + }; + + for (Callable testCase : cases) { + try { + testCase.call(); + fail(); + } catch (Exception e) { + String expected = + "java.lang.IllegalArgumentException: Field " + + testCase + + " can't use content-length-range"; + assertEquals(expected, e.toString()); + } + } + assertTrue(builder.build().getConditions().isEmpty()); + } + + @Test + public void testPostConditionsV4_toString() { + PostPolicyV4.PostConditionsV4.Builder builder = PostPolicyV4.PostConditionsV4.newBuilder(); + builder.addKeyCondition(PostPolicyV4.ConditionV4Type.MATCHES, "test-object"); + builder.addAclCondition(PostPolicyV4.ConditionV4Type.STARTS_WITH, "public"); + builder.addContentLengthRangeCondition(246, 266); + + Set toStringSet = new HashSet<>(); + for (PostPolicyV4.ConditionV4 conditionV4 : builder.build().getConditions()) { + toStringSet.add(conditionV4.toString()); + } + assertEquals(3, toStringSet.size()); + + String[] expectedStrings = { + "[\"eq\", \"$key\", \"test-object\"]", + "[\"starts-with\", \"$acl\", \"public\"]", + "[\"content-length-range\", 246, 266]" + }; + + for (String expected : expectedStrings) { + assertTrue(expected + "/" + toStringSet, toStringSet.contains(expected)); + } + } + + @Test + public void testPostPolicyV4Document_of_toJson() { + PostPolicyV4.PostConditionsV4 emptyConditions = + PostPolicyV4.PostConditionsV4.newBuilder().build(); + PostPolicyV4.PostPolicyV4Document emptyDocument = + PostPolicyV4.PostPolicyV4Document.of("", emptyConditions); + String emptyJson = emptyDocument.toJson(); + assertEquals(emptyJson, "{\"conditions\":[],\"expiration\":\"\"}"); + + PostPolicyV4.PostConditionsV4 postConditionsV4 = + PostPolicyV4.PostConditionsV4.newBuilder() + .addBucketCondition(PostPolicyV4.ConditionV4Type.MATCHES, "my-bucket") + .addKeyCondition(PostPolicyV4.ConditionV4Type.STARTS_WITH, "") + .addContentLengthRangeCondition(1, 1000) + .build(); + + String expiration = dateFormat.format(System.currentTimeMillis()); + PostPolicyV4.PostPolicyV4Document document = + PostPolicyV4.PostPolicyV4Document.of(expiration, postConditionsV4); + String json = document.toJson(); + assertEquals( + json, + "{\"conditions\":[{\"bucket\":\"my-bucket\"},[\"starts-with\",\"$key\",\"\"],[\"content-length-range\",1,1000]],\"expiration\":\"" + + expiration + + "\"}"); + } +} From 37cb632320c1b3448ff86cca93e64a828e9b900d Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Wed, 26 Aug 2020 09:31:44 -0700 Subject: [PATCH 30/35] build: temporarily disable reporting to unblock releases Source-Author: Stephanie Wang Source-Date: Tue Aug 25 13:05:26 2020 -0400 Source-Repo: googleapis/synthtool Source-Sha: 968465a1cad496e1292ef4584a054a35f756ff94 Source-Link: https://github.com/googleapis/synthtool/commit/968465a1cad496e1292ef4584a054a35f756ff94 --- .kokoro/release/stage.sh | 5 +++-- synth.metadata | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.kokoro/release/stage.sh b/.kokoro/release/stage.sh index 3c482cbc5..d19191fc8 100755 --- a/.kokoro/release/stage.sh +++ b/.kokoro/release/stage.sh @@ -16,8 +16,9 @@ set -eo pipefail # Start the releasetool reporter -python3 -m pip install gcp-releasetool -python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script +# Disable reporting due to issue observed with Kokoro blocking releases +# python3 -m pip install gcp-releasetool +# python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script source $(dirname "$0")/common.sh MAVEN_SETTINGS_FILE=$(realpath $(dirname "$0")/../../)/settings.xml diff --git a/synth.metadata b/synth.metadata index 31f1a9816..c283453cd 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-storage.git", - "sha": "6c372fa81e49ac74bdda6f9b10914fac42767247" + "sha": "8602b81eae95868e184fd4ab290396707bd21a8e" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "9602086c6c5b05db77950c7f7495a2a3868f3537" + "sha": "968465a1cad496e1292ef4584a054a35f756ff94" } } ], From 1e3e88a391651421469e5c7a8216a788eaa4ba5a Mon Sep 17 00:00:00 2001 From: Ajit Thakor <49403056+athakor@users.noreply.github.com> Date: Wed, 26 Aug 2020 23:34:29 +0530 Subject: [PATCH 31/35] feat: add support of daysSinceNoncurrentTime and noncurrentTimeBefore OLM options (#335) * feat: add support of daysSinceNoncurrentTime and noncurrentTimeBefore OLM options * feat: add javadoc * feat: change noncurrentTimeBefore type to date only * feat: update java doc --- .../com/google/cloud/storage/BucketInfo.java | 63 +++++++++++++++++-- .../google/cloud/storage/BucketInfoTest.java | 17 +++++ .../google/cloud/storage/StorageImplTest.java | 37 +++++++++++ .../cloud/storage/it/ITStorageTest.java | 4 ++ 4 files changed, 117 insertions(+), 4 deletions(-) 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 b3e96113b..038df91d9 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 @@ -351,7 +351,9 @@ public LifecycleRule(LifecycleAction action, LifecycleCondition condition) { && condition.getAge() == null && condition.getCreatedBefore() == null && condition.getMatchesStorageClass() == null - && condition.getNumberOfNewerVersions() == null) { + && condition.getNumberOfNewerVersions() == null + && condition.getDaysSinceNoncurrentTime() == null + && condition.getNoncurrentTimeBefore() == null) { throw new IllegalArgumentException( "You must specify at least one condition to use object lifecycle " + "management. Please see https://cloud.google.com/storage/docs/lifecycle for details."); @@ -419,7 +421,13 @@ Rule toPb() { ? null : transform( lifecycleCondition.getMatchesStorageClass(), - Functions.toStringFunction())); + Functions.toStringFunction())) + .setDaysSinceNoncurrentTime(lifecycleCondition.getDaysSinceNoncurrentTime()) + .setNoncurrentTimeBefore( + lifecycleCondition.getNoncurrentTimeBefore() == null + ? null + : new DateTime( + true, lifecycleCondition.getNoncurrentTimeBefore().getValue(), 0)); rule.setCondition(condition); @@ -462,7 +470,9 @@ static LifecycleRule fromPb(Rule rule) { public StorageClass apply(String storageClass) { return StorageClass.valueOf(storageClass); } - })); + })) + .setDaysSinceNoncurrentTime(condition.getDaysSinceNoncurrentTime()) + .setNoncurrentTimeBefore(condition.getNoncurrentTimeBefore()); return new LifecycleRule(lifecycleAction, conditionBuilder.build()); } @@ -480,6 +490,8 @@ public static class LifecycleCondition implements Serializable { private final Integer numberOfNewerVersions; private final Boolean isLive; private final List matchesStorageClass; + private final Integer daysSinceNoncurrentTime; + private final DateTime noncurrentTimeBefore; private LifecycleCondition(Builder builder) { this.age = builder.age; @@ -487,6 +499,8 @@ private LifecycleCondition(Builder builder) { this.numberOfNewerVersions = builder.numberOfNewerVersions; this.isLive = builder.isLive; this.matchesStorageClass = builder.matchesStorageClass; + this.daysSinceNoncurrentTime = builder.daysSinceNoncurrentTime; + this.noncurrentTimeBefore = builder.noncurrentTimeBefore; } public Builder toBuilder() { @@ -495,7 +509,9 @@ public Builder toBuilder() { .setCreatedBefore(this.createdBefore) .setNumberOfNewerVersions(this.numberOfNewerVersions) .setIsLive(this.isLive) - .setMatchesStorageClass(this.matchesStorageClass); + .setMatchesStorageClass(this.matchesStorageClass) + .setDaysSinceNoncurrentTime(this.daysSinceNoncurrentTime) + .setNoncurrentTimeBefore(this.noncurrentTimeBefore); } public static Builder newBuilder() { @@ -510,6 +526,8 @@ public String toString() { .add("numberofNewerVersions", numberOfNewerVersions) .add("isLive", isLive) .add("matchesStorageClass", matchesStorageClass) + .add("daysSinceNoncurrentTime", daysSinceNoncurrentTime) + .add("noncurrentTimeBefore", noncurrentTimeBefore) .toString(); } @@ -533,6 +551,18 @@ public List getMatchesStorageClass() { return matchesStorageClass; } + /** Returns the number of days elapsed since the noncurrent timestamp of an object. */ + public Integer getDaysSinceNoncurrentTime() { + return daysSinceNoncurrentTime; + } + + /** + * Returns the date in RFC 3339 format with only the date part (for instance, "2013-01-15"). + */ + public DateTime getNoncurrentTimeBefore() { + return noncurrentTimeBefore; + } + /** Builder for {@code LifecycleCondition}. */ public static class Builder { private Integer age; @@ -540,6 +570,8 @@ public static class Builder { private Integer numberOfNewerVersions; private Boolean isLive; private List matchesStorageClass; + private Integer daysSinceNoncurrentTime; + private DateTime noncurrentTimeBefore; private Builder() {} @@ -594,6 +626,29 @@ public Builder setMatchesStorageClass(List matchesStorageClass) { return this; } + /** + * Sets the number of days elapsed since the noncurrent timestamp of an object. The + * condition is satisfied if the days elapsed is at least this number. This condition is + * relevant only for versioned objects. The value of the field must be a nonnegative + * integer. If it's zero, the object version will become eligible for Lifecycle action as + * soon as it becomes noncurrent. + */ + public Builder setDaysSinceNoncurrentTime(Integer daysSinceNoncurrentTime) { + this.daysSinceNoncurrentTime = daysSinceNoncurrentTime; + return this; + } + + /** + * Sets the date in RFC 3339 format with only the date part (for instance, "2013-01-15"). + * Note that only date part will be considered, if the time is specified it will be + * truncated. This condition is satisfied when the noncurrent time on an object is before + * this date. This condition is relevant only for versioned objects. + */ + public Builder setNoncurrentTimeBefore(DateTime noncurrentTimeBefore) { + this.noncurrentTimeBefore = noncurrentTimeBefore; + return this; + } + /** Builds a {@code LifecycleCondition} object. * */ public LifecycleCondition build() { return new LifecycleCondition(this); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java index 425cfb702..c7b3aa198 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import com.google.api.client.util.DateTime; import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.Bucket.Lifecycle.Rule; import com.google.cloud.storage.Acl.Project; @@ -328,6 +329,22 @@ public void testLifecycleRules() { setStorageClassLifecycleRule.getAction().getStorageClass()); assertTrue(setStorageClassLifecycleRule.getCondition().getIsLive()); assertEquals(10, setStorageClassLifecycleRule.getCondition().getNumNewerVersions().intValue()); + + Rule lifecycleRule = + new LifecycleRule( + LifecycleAction.newSetStorageClassAction(StorageClass.COLDLINE), + LifecycleCondition.newBuilder() + .setIsLive(true) + .setNumberOfNewerVersions(10) + .setDaysSinceNoncurrentTime(30) + .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) + .build()) + .toPb(); + assertEquals(StorageClass.COLDLINE.toString(), lifecycleRule.getAction().getStorageClass()); + assertTrue(lifecycleRule.getCondition().getIsLive()); + assertEquals(10, lifecycleRule.getCondition().getNumNewerVersions().intValue()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); } @Test diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 24f5ed9e7..35c4e253c 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue; import com.google.api.client.googleapis.json.GoogleJsonError; +import com.google.api.client.util.DateTime; import com.google.api.core.ApiClock; import com.google.api.services.storage.model.Policy.Bindings; import com.google.api.services.storage.model.StorageObject; @@ -2232,4 +2233,40 @@ public void testV4PostPolicy() { assertEquals(outputFields.get("key"), "my-object"); assertEquals("https://storage.googleapis.com/my-bucket/", policy.getUrl()); } + + @Test + public void testBucketLifecycleRules() { + BucketInfo bucketInfo = + BucketInfo.newBuilder("b") + .setLocation("us") + .setLifecycleRules( + ImmutableList.of( + new BucketInfo.LifecycleRule( + BucketInfo.LifecycleRule.LifecycleAction.newSetStorageClassAction( + StorageClass.COLDLINE), + BucketInfo.LifecycleRule.LifecycleCondition.newBuilder() + .setAge(1) + .setNumberOfNewerVersions(3) + .setIsLive(false) + .setCreatedBefore(new DateTime(System.currentTimeMillis())) + .setMatchesStorageClass(ImmutableList.of(StorageClass.COLDLINE)) + .setDaysSinceNoncurrentTime(30) + .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) + .build()))) + .build(); + EasyMock.expect( + storageRpcMock.create(bucketInfo.toPb(), new HashMap())) + .andReturn(bucketInfo.toPb()); + EasyMock.replay(storageRpcMock); + initializeService(); + Bucket bucket = storage.create(bucketInfo); + BucketInfo.LifecycleRule lifecycleRule = bucket.getLifecycleRules().get(0); + assertEquals(3, lifecycleRule.getCondition().getNumberOfNewerVersions().intValue()); + assertNotNull(lifecycleRule.getCondition().getCreatedBefore()); + assertFalse(lifecycleRule.getCondition().getIsLive()); + assertEquals(1, lifecycleRule.getCondition().getAge().intValue()); + assertEquals(1, lifecycleRule.getCondition().getMatchesStorageClass().size()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); + } } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index a741f3eb7..d92ca5921 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -456,6 +456,8 @@ public void testGetBucketLifecycleRules() { .setIsLive(false) .setCreatedBefore(new DateTime(System.currentTimeMillis())) .setMatchesStorageClass(ImmutableList.of(StorageClass.COLDLINE)) + .setDaysSinceNoncurrentTime(30) + .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) .build()))) .build()); Bucket remoteBucket = @@ -472,6 +474,8 @@ public void testGetBucketLifecycleRules() { assertFalse(lifecycleRule.getCondition().getIsLive()); assertEquals(1, lifecycleRule.getCondition().getAge().intValue()); assertEquals(1, lifecycleRule.getCondition().getMatchesStorageClass().size()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); } finally { storage.delete(lifecycleTestBucketName); } From cf94230a5f02dcc16e364aa528d97046d80f59a0 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 26 Aug 2020 20:05:10 +0200 Subject: [PATCH 32/35] deps: update dependency com.google.api.grpc:grpc-google-cloud-kms-v1 to v0.86.1 (#463) --- google-cloud-storage/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 587822656..6db71ffd4 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -117,7 +117,7 @@ com.google.api.grpc grpc-google-cloud-kms-v1 - 0.86.0 + 0.86.1 test From 1af8288016f2526ddbe221ef22dc705e28b18b77 Mon Sep 17 00:00:00 2001 From: Ajit Thakor <49403056+athakor@users.noreply.github.com> Date: Thu, 27 Aug 2020 20:16:07 +0530 Subject: [PATCH 33/35] feat: add support of customTimeBefore and daysSinceCustomTime (#396) * feat: add support of customTimeBefore and daysSinceCustomTime * feat: add javadoc * feat: change customTimeBefore type to date only * feat: update javadoc * feat: updated java doc --- .../com/google/cloud/storage/BucketInfo.java | 58 +++++++++++++++++-- .../google/cloud/storage/BucketInfoTest.java | 5 ++ .../google/cloud/storage/StorageImplTest.java | 4 ++ .../cloud/storage/it/ITStorageTest.java | 4 ++ 4 files changed, 67 insertions(+), 4 deletions(-) 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 038df91d9..602be08ac 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 @@ -353,7 +353,9 @@ public LifecycleRule(LifecycleAction action, LifecycleCondition condition) { && condition.getMatchesStorageClass() == null && condition.getNumberOfNewerVersions() == null && condition.getDaysSinceNoncurrentTime() == null - && condition.getNoncurrentTimeBefore() == null) { + && condition.getNoncurrentTimeBefore() == null + && condition.getCustomTimeBefore() == null + && condition.getDaysSinceCustomTime() == null) { throw new IllegalArgumentException( "You must specify at least one condition to use object lifecycle " + "management. Please see https://cloud.google.com/storage/docs/lifecycle for details."); @@ -427,7 +429,12 @@ Rule toPb() { lifecycleCondition.getNoncurrentTimeBefore() == null ? null : new DateTime( - true, lifecycleCondition.getNoncurrentTimeBefore().getValue(), 0)); + true, lifecycleCondition.getNoncurrentTimeBefore().getValue(), 0)) + .setCustomTimeBefore( + lifecycleCondition.getCustomTimeBefore() == null + ? null + : new DateTime(true, lifecycleCondition.getCustomTimeBefore().getValue(), 0)) + .setDaysSinceCustomTime(lifecycleCondition.getDaysSinceCustomTime()); rule.setCondition(condition); @@ -472,7 +479,9 @@ public StorageClass apply(String storageClass) { } })) .setDaysSinceNoncurrentTime(condition.getDaysSinceNoncurrentTime()) - .setNoncurrentTimeBefore(condition.getNoncurrentTimeBefore()); + .setNoncurrentTimeBefore(condition.getNoncurrentTimeBefore()) + .setCustomTimeBefore(condition.getCustomTimeBefore()) + .setDaysSinceCustomTime(condition.getDaysSinceCustomTime()); return new LifecycleRule(lifecycleAction, conditionBuilder.build()); } @@ -492,6 +501,8 @@ public static class LifecycleCondition implements Serializable { private final List matchesStorageClass; private final Integer daysSinceNoncurrentTime; private final DateTime noncurrentTimeBefore; + private final DateTime customTimeBefore; + private final Integer daysSinceCustomTime; private LifecycleCondition(Builder builder) { this.age = builder.age; @@ -501,6 +512,8 @@ private LifecycleCondition(Builder builder) { this.matchesStorageClass = builder.matchesStorageClass; this.daysSinceNoncurrentTime = builder.daysSinceNoncurrentTime; this.noncurrentTimeBefore = builder.noncurrentTimeBefore; + this.customTimeBefore = builder.customTimeBefore; + this.daysSinceCustomTime = builder.daysSinceCustomTime; } public Builder toBuilder() { @@ -511,7 +524,9 @@ public Builder toBuilder() { .setIsLive(this.isLive) .setMatchesStorageClass(this.matchesStorageClass) .setDaysSinceNoncurrentTime(this.daysSinceNoncurrentTime) - .setNoncurrentTimeBefore(this.noncurrentTimeBefore); + .setNoncurrentTimeBefore(this.noncurrentTimeBefore) + .setCustomTimeBefore(this.customTimeBefore) + .setDaysSinceCustomTime(this.daysSinceCustomTime); } public static Builder newBuilder() { @@ -528,6 +543,8 @@ public String toString() { .add("matchesStorageClass", matchesStorageClass) .add("daysSinceNoncurrentTime", daysSinceNoncurrentTime) .add("noncurrentTimeBefore", noncurrentTimeBefore) + .add("customTimeBefore", customTimeBefore) + .add("daysSinceCustomTime", daysSinceCustomTime) .toString(); } @@ -563,6 +580,16 @@ public DateTime getNoncurrentTimeBefore() { return noncurrentTimeBefore; } + /* Returns the date in RFC 3339 format with only the date part (for instance, "2013-01-15").*/ + public DateTime getCustomTimeBefore() { + return customTimeBefore; + } + + /** Returns the number of days elapsed since the user-specified timestamp set on an object. */ + public Integer getDaysSinceCustomTime() { + return daysSinceCustomTime; + } + /** Builder for {@code LifecycleCondition}. */ public static class Builder { private Integer age; @@ -572,6 +599,8 @@ public static class Builder { private List matchesStorageClass; private Integer daysSinceNoncurrentTime; private DateTime noncurrentTimeBefore; + private DateTime customTimeBefore; + private Integer daysSinceCustomTime; private Builder() {} @@ -649,6 +678,27 @@ public Builder setNoncurrentTimeBefore(DateTime noncurrentTimeBefore) { return this; } + /** + * Sets the date in RFC 3339 format with only the date part (for instance, "2013-01-15"). + * Note that only date part will be considered, if the time is specified it will be + * truncated. This condition is satisfied when the custom time on an object is before this + * date in UTC. + */ + public Builder setCustomTimeBefore(DateTime customTimeBefore) { + this.customTimeBefore = customTimeBefore; + return this; + } + + /** + * Sets the number of days elapsed since the user-specified timestamp set on an object. The + * condition is satisfied if the days elapsed is at least this number. If no custom + * timestamp is specified on an object, the condition does not apply. + */ + public Builder setDaysSinceCustomTime(Integer daysSinceCustomTime) { + this.daysSinceCustomTime = daysSinceCustomTime; + return this; + } + /** Builds a {@code LifecycleCondition} object. * */ public LifecycleCondition build() { return new LifecycleCondition(this); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java index c7b3aa198..8def7c306 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java @@ -338,6 +338,8 @@ public void testLifecycleRules() { .setNumberOfNewerVersions(10) .setDaysSinceNoncurrentTime(30) .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) + .setCustomTimeBefore(new DateTime(System.currentTimeMillis())) + .setDaysSinceCustomTime(30) .build()) .toPb(); assertEquals(StorageClass.COLDLINE.toString(), lifecycleRule.getAction().getStorageClass()); @@ -345,6 +347,9 @@ public void testLifecycleRules() { assertEquals(10, lifecycleRule.getCondition().getNumNewerVersions().intValue()); assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); + assertEquals(StorageClass.COLDLINE.toString(), lifecycleRule.getAction().getStorageClass()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceCustomTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getCustomTimeBefore()); } @Test diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 35c4e253c..590fe37af 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -2252,6 +2252,8 @@ public void testBucketLifecycleRules() { .setMatchesStorageClass(ImmutableList.of(StorageClass.COLDLINE)) .setDaysSinceNoncurrentTime(30) .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) + .setCustomTimeBefore(new DateTime(System.currentTimeMillis())) + .setDaysSinceCustomTime(30) .build()))) .build(); EasyMock.expect( @@ -2268,5 +2270,7 @@ public void testBucketLifecycleRules() { assertEquals(1, lifecycleRule.getCondition().getMatchesStorageClass().size()); assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceCustomTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getCustomTimeBefore()); } } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index d92ca5921..52947795b 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -458,6 +458,8 @@ public void testGetBucketLifecycleRules() { .setMatchesStorageClass(ImmutableList.of(StorageClass.COLDLINE)) .setDaysSinceNoncurrentTime(30) .setNoncurrentTimeBefore(new DateTime(System.currentTimeMillis())) + .setCustomTimeBefore(new DateTime(System.currentTimeMillis())) + .setDaysSinceCustomTime(30) .build()))) .build()); Bucket remoteBucket = @@ -476,6 +478,8 @@ public void testGetBucketLifecycleRules() { assertEquals(1, lifecycleRule.getCondition().getMatchesStorageClass().size()); assertEquals(30, lifecycleRule.getCondition().getDaysSinceNoncurrentTime().intValue()); assertNotNull(lifecycleRule.getCondition().getNoncurrentTimeBefore()); + assertEquals(30, lifecycleRule.getCondition().getDaysSinceCustomTime().intValue()); + assertNotNull(lifecycleRule.getCondition().getCustomTimeBefore()); } finally { storage.delete(lifecycleTestBucketName); } From 6f4585eb6706390865cf5fb565fa8062d0071045 Mon Sep 17 00:00:00 2001 From: Ajit Thakor <49403056+athakor@users.noreply.github.com> Date: Fri, 28 Aug 2020 04:28:34 +0530 Subject: [PATCH 34/35] feat: add support of customTime metadata (#413) * feat: support customTime metadata * feat: add javaDoc * feat: address few nits * feat: fix the api breaking change * Add exception to avoid breaking change * Add exception to avoid breaking change Co-authored-by: Jesse Lovelace --- .../java/com/google/cloud/storage/Blob.java | 6 +++ .../com/google/cloud/storage/BlobInfo.java | 41 +++++++++++++++++++ .../google/cloud/storage/BlobInfoTest.java | 6 +++ .../com/google/cloud/storage/BlobTest.java | 5 +++ .../cloud/storage/it/ITStorageTest.java | 4 +- 5 files changed, 61 insertions(+), 1 deletion(-) 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 679b410a6..398eb3166 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 @@ -434,6 +434,12 @@ Builder setCreateTime(Long createTime) { return this; } + @Override + public Builder setCustomTime(Long customTime) { + infoBuilder.setCustomTime(customTime); + return this; + } + @Override Builder setIsDirectory(boolean isDirectory) { infoBuilder.setIsDirectory(isDirectory); 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 ef38ba033..67256d1c7 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 @@ -84,6 +84,7 @@ public StorageObject apply(BlobInfo blobInfo) { private final String etag; private final String md5; private final String crc32c; + private final Long customTime; private final String mediaLink; private final Map metadata; private final Long metageneration; @@ -260,6 +261,26 @@ public abstract static class Builder { */ public abstract Builder setCrc32c(String crc32c); + /** + * Sets the custom time for an object. Once set it can't be unset and only changed to a custom + * datetime in the future. To unset the custom time, you must either perform a rewrite operation + * or upload the data again. + * + *

Example of setting the custom time. + * + *

{@code
+     * String bucketName = "my-unique-bucket";
+     * String blobName = "my-blob-name";
+     * long customTime = 1598423868301L;
+     * BlobInfo blob = BlobInfo.newBuilder(bucketName, blobName).setCustomTime(customTime).build();
+     * }
+ */ + public Builder setCustomTime(Long customTime) { + throw new UnsupportedOperationException( + "Override setCustomTime with your own implementation," + + " or use com.google.cloud.storage.Blob."); + } + /** * Sets the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; from hex @@ -325,6 +346,7 @@ static final class BuilderImpl extends Builder { private String selfLink; private String md5; private String crc32c; + private Long customTime; private String mediaLink; private Map metadata; private Long metageneration; @@ -360,6 +382,7 @@ static final class BuilderImpl extends Builder { selfLink = blobInfo.selfLink; md5 = blobInfo.md5; crc32c = blobInfo.crc32c; + customTime = blobInfo.customTime; mediaLink = blobInfo.mediaLink; metadata = blobInfo.metadata; metageneration = blobInfo.metageneration; @@ -488,6 +511,12 @@ public Builder setCrc32c(String crc32c) { return this; } + @Override + public Builder setCustomTime(Long customTime) { + this.customTime = customTime; + return this; + } + @Override public Builder setCrc32cFromHexString(String crc32cHexString) { if (crc32cHexString == null) { @@ -619,6 +648,7 @@ public BlobInfo build() { selfLink = builder.selfLink; md5 = builder.md5; crc32c = builder.crc32c; + customTime = builder.customTime; mediaLink = builder.mediaLink; metadata = builder.metadata; metageneration = builder.metageneration; @@ -857,6 +887,11 @@ public Long getCreateTime() { return createTime; } + /** Returns the custom time specified by the user for an object. */ + public Long getCustomTime() { + return customTime; + } + /** * Returns {@code true} if the current blob represents a directory. This can only happen if the * blob is returned by {@link Storage#list(String, Storage.BlobListOption...)} when the {@link @@ -1002,6 +1037,9 @@ public ObjectAccessControl apply(Acl acl) { if (createTime != null) { storageObject.setTimeCreated(new DateTime(createTime)); } + if (customTime != null) { + storageObject.setCustomTime(new DateTime(customTime)); + } if (size != null) { storageObject.setSize(BigInteger.valueOf(size)); } @@ -1125,6 +1163,9 @@ static BlobInfo fromPb(StorageObject storageObject) { if (storageObject.getTimeCreated() != null) { builder.setCreateTime(storageObject.getTimeCreated().getValue()); } + if (storageObject.getCustomTime() != null) { + builder.setCustomTime(storageObject.getCustomTime().getValue()); + } if (storageObject.getSize() != null) { builder.setSize(storageObject.getSize().longValue()); } 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 ea4f6dbda..d4119971f 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 @@ -67,6 +67,7 @@ public class BlobInfoTest { private static final Long SIZE = 1024L; private static final Long UPDATE_TIME = DELETE_TIME - 1L; private static final Long CREATE_TIME = UPDATE_TIME - 1L; + private static final Long CUSTOM_TIME = CREATE_TIME - 1L; private static final String ENCRYPTION_ALGORITHM = "AES256"; private static final String KEY_SHA256 = "keySha"; private static final CustomerEncryption CUSTOMER_ENCRYPTION = @@ -101,6 +102,7 @@ public class BlobInfoTest { .setSize(SIZE) .setUpdateTime(UPDATE_TIME) .setCreateTime(CREATE_TIME) + .setCustomTime(CUSTOM_TIME) .setStorageClass(STORAGE_CLASS) .setKmsKeyName(KMS_KEY_NAME) .setEventBasedHold(EVENT_BASED_HOLD) @@ -195,6 +197,7 @@ public void testBuilder() { assertEquals(SIZE, BLOB_INFO.getSize()); assertEquals(UPDATE_TIME, BLOB_INFO.getUpdateTime()); assertEquals(CREATE_TIME, BLOB_INFO.getCreateTime()); + assertEquals(CUSTOM_TIME, BLOB_INFO.getCustomTime()); assertEquals(STORAGE_CLASS, BLOB_INFO.getStorageClass()); assertEquals(KMS_KEY_NAME, BLOB_INFO.getKmsKeyName()); assertEquals(EVENT_BASED_HOLD, BLOB_INFO.getEventBasedHold()); @@ -214,6 +217,7 @@ public void testBuilder() { assertNull(DIRECTORY_INFO.getCrc32c()); assertNull(DIRECTORY_INFO.getCrc32cToHexString()); assertNull(DIRECTORY_INFO.getCreateTime()); + assertNull(DIRECTORY_INFO.getCustomTime()); assertNull(DIRECTORY_INFO.getDeleteTime()); assertNull(DIRECTORY_INFO.getEtag()); assertNull(DIRECTORY_INFO.getGeneration()); @@ -257,6 +261,7 @@ private void compareBlobs(BlobInfo expected, BlobInfo value) { assertEquals(expected.getOwner(), value.getOwner()); assertEquals(expected.getSelfLink(), value.getSelfLink()); assertEquals(expected.getSize(), value.getSize()); + assertEquals(expected.getCustomTime(), value.getCustomTime()); assertEquals(expected.getUpdateTime(), value.getUpdateTime()); assertEquals(expected.getStorageClass(), value.getStorageClass()); assertEquals(expected.getKmsKeyName(), value.getKmsKeyName()); @@ -299,6 +304,7 @@ public void testToPbAndFromPb() { assertNull(blobInfo.getCrc32c()); assertNull(blobInfo.getCrc32cToHexString()); assertNull(blobInfo.getCreateTime()); + assertNull(blobInfo.getCustomTime()); assertNull(blobInfo.getDeleteTime()); assertNull(blobInfo.getEtag()); assertNull(blobInfo.getGeneration()); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobTest.java index 7ff384fb9..2ff21b712 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BlobTest.java @@ -91,6 +91,7 @@ public class BlobTest { private static final Long SIZE = 1024L; private static final Long UPDATE_TIME = DELETE_TIME - 1L; private static final Long CREATE_TIME = UPDATE_TIME - 1L; + private static final Long CUSTOM_TIME = CREATE_TIME - 1L; private static final String ENCRYPTION_ALGORITHM = "AES256"; private static final String KEY_SHA256 = "keySha"; private static final BlobInfo.CustomerEncryption CUSTOMER_ENCRYPTION = @@ -122,6 +123,7 @@ public class BlobTest { .setSize(SIZE) .setUpdateTime(UPDATE_TIME) .setCreateTime(CREATE_TIME) + .setCustomTime(CUSTOM_TIME) .setCustomerEncryption(CUSTOMER_ENCRYPTION) .setKmsKeyName(KMS_KEY_NAME) .setEventBasedHold(EVENT_BASED_HOLD) @@ -510,6 +512,7 @@ public void testBuilder() { .setContentLanguage(CONTENT_LANGUAGE) .setCrc32c(CRC32) .setCreateTime(CREATE_TIME) + .setCustomTime(CUSTOM_TIME) .setCustomerEncryption(CUSTOMER_ENCRYPTION) .setKmsKeyName(KMS_KEY_NAME) .setEventBasedHold(EVENT_BASED_HOLD) @@ -539,6 +542,7 @@ public void testBuilder() { assertEquals(CRC32, blob.getCrc32c()); assertEquals(CRC32_HEX_STRING, blob.getCrc32cToHexString()); assertEquals(CREATE_TIME, blob.getCreateTime()); + assertEquals(CUSTOM_TIME, blob.getCustomTime()); assertEquals(CUSTOMER_ENCRYPTION, blob.getCustomerEncryption()); assertEquals(KMS_KEY_NAME, blob.getKmsKeyName()); assertEquals(EVENT_BASED_HOLD, blob.getEventBasedHold()); @@ -589,6 +593,7 @@ public void testBuilder() { assertNull(blob.getSelfLink()); assertEquals(0L, (long) blob.getSize()); assertNull(blob.getUpdateTime()); + assertNull(blob.getCustomTime()); assertTrue(blob.isDirectory()); } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index 52947795b..925e3d6d1 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -527,9 +527,11 @@ public void testUpdateBucketDefaultKmsKeyName() throws ExecutionException, Inter @Test public void testCreateBlob() { String blobName = "test-create-blob"; - BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build(); + BlobInfo blob = + BlobInfo.newBuilder(BUCKET, blobName).setCustomTime(System.currentTimeMillis()).build(); Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT); assertNotNull(remoteBlob); + assertNotNull(remoteBlob.getCustomTime()); assertEquals(blob.getBucket(), remoteBlob.getBucket()); assertEquals(blob.getName(), remoteBlob.getName()); byte[] readBytes = storage.readAllBytes(BUCKET, blobName); From 9835c29eb645dc988f899733fba2aabca736e319 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 16:51:58 -0700 Subject: [PATCH 35/35] chore: release 1.112.0 (#436) * chore: updated CHANGELOG.md [ci skip] * chore: updated README.md [ci skip] * chore: updated versions.txt [ci skip] * chore: updated samples/pom.xml [ci skip] * chore: updated samples/install-without-bom/pom.xml [ci skip] * chore: updated samples/snapshot/pom.xml [ci skip] * chore: updated pom.xml [ci skip] * chore: updated samples/snippets/pom.xml [ci skip] * chore: updated google-cloud-storage/pom.xml Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ README.md | 4 ++-- google-cloud-storage/pom.xml | 4 ++-- pom.xml | 2 +- versions.txt | 2 +- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38658d9d9..996cdcd05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## [1.112.0](https://www.github.com/googleapis/java-storage/compare/v1.111.2...v1.112.0) (2020-08-27) + + +### Features + +* add support of customTime metadata ([#413](https://www.github.com/googleapis/java-storage/issues/413)) ([6f4585e](https://www.github.com/googleapis/java-storage/commit/6f4585eb6706390865cf5fb565fa8062d0071045)) +* add support of customTimeBefore and daysSinceCustomTime ([#396](https://www.github.com/googleapis/java-storage/issues/396)) ([1af8288](https://www.github.com/googleapis/java-storage/commit/1af8288016f2526ddbe221ef22dc705e28b18b77)) +* add support of daysSinceNoncurrentTime and noncurrentTimeBefore OLM options ([#335](https://www.github.com/googleapis/java-storage/issues/335)) ([1e3e88a](https://www.github.com/googleapis/java-storage/commit/1e3e88a391651421469e5c7a8216a788eaa4ba5a)) +* add support of null to remove the CORS configuration from bucket ([#438](https://www.github.com/googleapis/java-storage/issues/438)) ([f8a4b12](https://www.github.com/googleapis/java-storage/commit/f8a4b12517c661881d7b7c65f796c1c8f1cf3ae9)) +* add support of startOffset and endOffset ([#430](https://www.github.com/googleapis/java-storage/issues/430)) ([38c1c34](https://www.github.com/googleapis/java-storage/commit/38c1c34937eeacd126cf6d62bf85fb9db90e1702)) +* auto content-type on blob creation ([#338](https://www.github.com/googleapis/java-storage/issues/338)) ([66d1eb7](https://www.github.com/googleapis/java-storage/commit/66d1eb793383b9e83992824b392cedd28d54609f)) +* expose updateTime field of the bucket ([#449](https://www.github.com/googleapis/java-storage/issues/449)) ([f0e945e](https://www.github.com/googleapis/java-storage/commit/f0e945e14662b86594298557b83151d3cb7e1ebb)) + + +### Bug Fixes + +* Ignore CONTRIBUTING.md ([#447](https://www.github.com/googleapis/java-storage/issues/447)) ([bdacdc9](https://www.github.com/googleapis/java-storage/commit/bdacdc93a107108add5bd9dc00473997534aa761)), closes [#446](https://www.github.com/googleapis/java-storage/issues/446) [#446](https://www.github.com/googleapis/java-storage/issues/446) +* PostPolicyV4 classes could be improved ([#442](https://www.github.com/googleapis/java-storage/issues/442)) ([8602b81](https://www.github.com/googleapis/java-storage/commit/8602b81eae95868e184fd4ab290396707bd21a8e)) +* **docs:** example of Storage#testIamPermissions ([#434](https://www.github.com/googleapis/java-storage/issues/434)) ([275f452](https://www.github.com/googleapis/java-storage/commit/275f452a5993f95a84fb603a5f4b436238b39439)) +* PostPolicyV4.PostFieldsV4.Builder.addCustomMetadataField() allows to add prefixed an not prefixed custom fields ([#398](https://www.github.com/googleapis/java-storage/issues/398)) ([02dc3b5](https://www.github.com/googleapis/java-storage/commit/02dc3b5e5377d8848c889647e72102cd9acc646d)) + + +### Dependencies + +* update dependency com.google.api-client:google-api-client to v1.30.10 ([#423](https://www.github.com/googleapis/java-storage/issues/423)) ([fbfa9ec](https://www.github.com/googleapis/java-storage/commit/fbfa9ecf277794e07d9a3c46d5b5022f54c37afd)) +* update dependency com.google.api.grpc:grpc-google-cloud-kms-v1 to v0.86.1 ([#463](https://www.github.com/googleapis/java-storage/issues/463)) ([cf94230](https://www.github.com/googleapis/java-storage/commit/cf94230a5f02dcc16e364aa528d97046d80f59a0)) +* update dependency com.google.api.grpc:proto-google-cloud-kms-v1 to v0.86.1 ([#464](https://www.github.com/googleapis/java-storage/issues/464)) ([6c372fa](https://www.github.com/googleapis/java-storage/commit/6c372fa81e49ac74bdda6f9b10914fac42767247)) +* update dependency com.google.apis:google-api-services-storage to v1-rev20200611-1.30.10 ([#428](https://www.github.com/googleapis/java-storage/issues/428)) ([6ef57eb](https://www.github.com/googleapis/java-storage/commit/6ef57ebc9eeddc90f13ef87274e8ab0b7eb53290)) +* update dependency com.google.apis:google-api-services-storage to v1-rev20200727-1.30.10 ([#457](https://www.github.com/googleapis/java-storage/issues/457)) ([edfd1e6](https://www.github.com/googleapis/java-storage/commit/edfd1e69e886adb04b98b54b3a63768c7e82b1e0)) +* update dependency com.google.cloud:google-cloud-shared-dependencies to v0.8.4 ([#452](https://www.github.com/googleapis/java-storage/issues/452)) ([12bc02d](https://www.github.com/googleapis/java-storage/commit/12bc02d7bc05e584cad4362628155333630fbcba)) +* update dependency com.google.cloud:google-cloud-shared-dependencies to v0.8.6 ([#458](https://www.github.com/googleapis/java-storage/issues/458)) ([f8d6e15](https://www.github.com/googleapis/java-storage/commit/f8d6e158a06aec926fb7bc42f10483d56696a37e)) + ### [1.111.2](https://www.github.com/googleapis/java-storage/compare/v1.111.1...v1.111.2) (2020-07-10) diff --git a/README.md b/README.md index 349cb1689..288383da5 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,11 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-storage:1.111.2' +compile 'com.google.cloud:google-cloud-storage:1.112.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "1.111.2" +libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "1.112.0" ``` [//]: # ({x-version-update-end}) diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 6db71ffd4..780862d6b 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-storage - 1.111.3-SNAPSHOT + 1.112.0 jar Google Cloud Storage https://github.com/googleapis/java-storage @@ -12,7 +12,7 @@ com.google.cloud google-cloud-storage-parent - 1.111.3-SNAPSHOT + 1.112.0 google-cloud-storage diff --git a/pom.xml b/pom.xml index 3672201db..80c13fb10 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-storage-parent pom - 1.111.3-SNAPSHOT + 1.112.0 Storage Parent https://github.com/googleapis/java-storage diff --git a/versions.txt b/versions.txt index ef36ef925..4e7fd1db9 100644 --- a/versions.txt +++ b/versions.txt @@ -1,4 +1,4 @@ # Format: # module:released-version:current-version -google-cloud-storage:1.111.2:1.111.3-SNAPSHOT \ No newline at end of file +google-cloud-storage:1.112.0:1.112.0 \ No newline at end of file