diff --git a/google-cloud-storage/clirr-ignored-differences.xml b/google-cloud-storage/clirr-ignored-differences.xml index e45beaea0..1afd91a66 100644 --- a/google-cloud-storage/clirr-ignored-differences.xml +++ b/google-cloud-storage/clirr-ignored-differences.xml @@ -7,6 +7,12 @@ * setCustomPlacementConfig(com.google.cloud.storage.BucketInfo$CustomPlacementConfig) + + com/google/cloud/storage/BucketInfo* + 7013 + * setAutoclass(com.google.cloud.storage.BucketInfo$Autoclass) + + 7002 com/google/cloud/storage/HmacKey$HmacKeyMetadata diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java index d876be88e..d5ca3b594 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java @@ -53,6 +53,7 @@ import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.BlobInfo.CustomerEncryption; +import com.google.cloud.storage.BucketInfo.Autoclass; import com.google.cloud.storage.BucketInfo.CustomPlacementConfig; import com.google.cloud.storage.BucketInfo.IamConfiguration; import com.google.cloud.storage.BucketInfo.LifecycleRule; @@ -101,6 +102,8 @@ final class ApiaryConversions { Codec.of(this::loggingEncode, this::loggingDecode); private final Codec iamConfigurationCodec = Codec.of(this::iamConfigEncode, this::iamConfigDecode); + private final Codec autoclassCodec = + Codec.of(this::autoclassEncode, this::autoclassDecode); private final Codec lifecycleRuleCodec = Codec.of(this::lifecycleRuleEncode, this::lifecycleRuleDecode); private final Codec lifecycleConditionCodec = @@ -386,6 +389,7 @@ private Bucket bucketInfoEncode(BucketInfo from) { to.setRetentionPolicy(retentionPolicy); } ifNonNull(from.getIamConfiguration(), this::iamConfigEncode, to::setIamConfiguration); + ifNonNull(from.getAutoclass(), this::autoclassEncode, to::setAutoclass); ifNonNull(from.getLogging(), this::loggingEncode, to::setLogging); ifNonNull( from.getCustomPlacementConfig(), @@ -438,6 +442,7 @@ private BucketInfo bucketInfoDecode(com.google.api.services.storage.model.Bucket ifNonNull(retentionPolicy, RetentionPolicy::getIsLocked, to::setRetentionPolicyIsLocked); ifNonNull(retentionPolicy, RetentionPolicy::getRetentionPeriod, to::setRetentionPeriod); ifNonNull(from.getIamConfiguration(), this::iamConfigDecode, to::setIamConfiguration); + ifNonNull(from.getAutoclass(), this::autoclassDecode, to::setAutoclass); ifNonNull(from.getLogging(), this::loggingDecode, to::setLogging); ifNonNull( from.getCustomPlacementConfig(), @@ -473,6 +478,20 @@ private IamConfiguration iamConfigDecode(Bucket.IamConfiguration from) { return to.build(); } + private Bucket.Autoclass autoclassEncode(Autoclass from) { + Bucket.Autoclass to = new Bucket.Autoclass(); + ifNonNull(from.getEnabled(), to::setEnabled); + ifNonNull(from.getToggleTime(), dateTimeCodec::encode, to::setToggleTime); + return to; + } + + private Autoclass autoclassDecode(Bucket.Autoclass from) { + Autoclass.Builder to = Autoclass.newBuilder(); + to.setEnabled(from.getEnabled()); + ifNonNull(from.getToggleTime(), dateTimeCodec::decode, to::setToggleTime); + return to.build(); + } + private UniformBucketLevelAccess ublaEncode(IamConfiguration from) { UniformBucketLevelAccess to = new UniformBucketLevelAccess(); to.setEnabled(from.isUniformBucketLevelAccessEnabled()); 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 c74e784d3..cbfbf5251 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 @@ -603,6 +603,12 @@ public Builder setIamConfiguration(IamConfiguration iamConfiguration) { return this; } + @Override + public Builder setAutoclass(Autoclass autoclass) { + infoBuilder.setAutoclass(autoclass); + return this; + } + @Override public Builder setLogging(Logging logging) { infoBuilder.setLogging(logging); 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 60a330453..8857f55d3 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 @@ -107,6 +107,7 @@ public class BucketInfo implements Serializable { private final Boolean retentionPolicyIsLocked; private final Duration retentionPeriod; private final IamConfiguration iamConfiguration; + private final Autoclass autoclass; private final String locationType; private final Logging logging; private final CustomPlacementConfig customPlacementConfig; @@ -340,6 +341,89 @@ public String toString() { } } + public static final class Autoclass implements Serializable { + + private static final long serialVersionUID = -2378172222188072439L; + private Boolean enabled; + private OffsetDateTime toggleTime; + + public Boolean getEnabled() { + return enabled; + } + + public OffsetDateTime getToggleTime() { + return toggleTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Autoclass)) { + return false; + } + Autoclass that = (Autoclass) o; + return Objects.equals(enabled, that.enabled) && Objects.equals(toggleTime, that.toggleTime); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, toggleTime); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("enabled", enabled) + .add("toggleTime", toggleTime) + .toString(); + } + + private Autoclass() {} + + private Autoclass(Builder builder) { + this.enabled = builder.enabled; + this.toggleTime = builder.toggleTime; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Builder toBuilder() { + return newBuilder().setEnabled(enabled).setToggleTime(toggleTime); + } + + public static final class Builder { + private Boolean enabled; + private OffsetDateTime toggleTime; + + /** + * Sets whether Autoclass is enabled for this bucket. Currently, autoclass can only be enabled + * at bucket create time. Any calls to update an existing Autoclass configuration must be to + * disable it, calls to enable Autoclass on an existing bucket will fail. + */ + public Builder setEnabled(Boolean enabled) { + this.enabled = enabled; + return this; + } + + /** + * Sets the last time autoclass was toggled on or off. Set to package private because this + * should only be set by the backend. + */ + Builder setToggleTime(OffsetDateTime toggleTime) { + this.toggleTime = toggleTime; + return this; + } + + public Autoclass build() { + return new Autoclass(this); + } + } + } + /** * The bucket's custom placement configuration for Custom Dual Regions. If using `location` is * also required. @@ -1443,6 +1527,8 @@ public Builder setRetentionPeriodDuration(Duration retentionPeriod) { @BetaApi public abstract Builder setIamConfiguration(IamConfiguration iamConfiguration); + public abstract Builder setAutoclass(Autoclass autoclass); + public abstract Builder setLogging(Logging logging); public abstract Builder setCustomPlacementConfig(CustomPlacementConfig customPlacementConfig); @@ -1540,6 +1626,7 @@ static final class BuilderImpl extends Builder { private Boolean retentionPolicyIsLocked; private Duration retentionPeriod; private IamConfiguration iamConfiguration; + private Autoclass autoclass; private String locationType; private Logging logging; private CustomPlacementConfig customPlacementConfig; @@ -1577,6 +1664,7 @@ static final class BuilderImpl extends Builder { retentionPolicyIsLocked = bucketInfo.retentionPolicyIsLocked; retentionPeriod = bucketInfo.retentionPeriod; iamConfiguration = bucketInfo.iamConfiguration; + autoclass = bucketInfo.autoclass; locationType = bucketInfo.locationType; logging = bucketInfo.logging; customPlacementConfig = bucketInfo.customPlacementConfig; @@ -1910,6 +1998,15 @@ public Builder setIamConfiguration(IamConfiguration iamConfiguration) { return this; } + @Override + public Builder setAutoclass(Autoclass autoclass) { + if (!Objects.equals(this.autoclass, autoclass)) { + modifiedFields.add(BucketField.AUTOCLASS); + } + this.autoclass = autoclass; + return this; + } + @Override public Builder setLogging(Logging logging) { Logging tmp = logging != null ? logging : Logging.newBuilder().build(); @@ -2165,6 +2262,7 @@ private Builder clearDeleteLifecycleRules() { retentionPolicyIsLocked = builder.retentionPolicyIsLocked; retentionPeriod = builder.retentionPeriod; iamConfiguration = builder.iamConfiguration; + autoclass = builder.autoclass; locationType = builder.locationType; logging = builder.logging; customPlacementConfig = builder.customPlacementConfig; @@ -2487,6 +2585,11 @@ public IamConfiguration getIamConfiguration() { return iamConfiguration; } + /** Returns the Autoclass configuration */ + public Autoclass getAutoclass() { + return autoclass; + } + /** Returns the Logging */ public Logging getLogging() { return logging; @@ -2531,6 +2634,7 @@ public int hashCode() { retentionPolicyIsLocked, retentionPeriod, iamConfiguration, + autoclass, locationType, logging); } @@ -2570,6 +2674,7 @@ public boolean equals(Object o) { && Objects.equals(retentionPolicyIsLocked, that.retentionPolicyIsLocked) && Objects.equals(retentionPeriod, that.retentionPeriod) && Objects.equals(iamConfiguration, that.iamConfiguration) + && Objects.equals(autoclass, that.autoclass) && Objects.equals(locationType, that.locationType) && Objects.equals(logging, that.logging); } diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java index dab56a647..a6e02608e 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java @@ -82,6 +82,8 @@ final class GrpcConversions { Codec.of(this::loggingEncode, this::loggingDecode); private final Codec iamConfigurationCodec = Codec.of(this::iamConfigEncode, this::iamConfigDecode); + private final Codec autoclassCodec = + Codec.of(this::autoclassEncode, this::autoclassDecode); private final Codec lifecycleRuleCodec = Codec.of(this::lifecycleRuleEncode, this::lifecycleRuleDecode); private final Codec bucketInfoCodec = @@ -276,6 +278,9 @@ private BucketInfo bucketInfoDecode(Bucket from) { if (from.hasIamConfig()) { to.setIamConfiguration(iamConfigurationCodec.decode(from.getIamConfig())); } + if (from.hasAutoclass()) { + to.setAutoclass(autoclassCodec.decode(from.getAutoclass())); + } if (from.hasCustomPlacementConfig()) { Bucket.CustomPlacementConfig customPlacementConfig = from.getCustomPlacementConfig(); to.setCustomPlacementConfig( @@ -364,6 +369,7 @@ private Bucket bucketInfoEncode(BucketInfo from) { to::addAllDefaultObjectAcl); ifNonNull(from.getAcl(), toImmutableListOf(bucketAclCodec::encode), to::addAllAcl); ifNonNull(from.getIamConfiguration(), iamConfigurationCodec::encode, to::setIamConfig); + ifNonNull(from.getAutoclass(), autoclassCodec::encode, to::setAutoclass); CustomPlacementConfig customPlacementConfig = from.getCustomPlacementConfig(); if (customPlacementConfig != null && customPlacementConfig.getDataLocations() != null) { to.setCustomPlacementConfig( @@ -516,6 +522,20 @@ private Bucket.IamConfig.UniformBucketLevelAccess ublaEncode(BucketInfo.IamConfi return to.build(); } + private BucketInfo.Autoclass autoclassDecode(Bucket.Autoclass from) { + BucketInfo.Autoclass.Builder to = BucketInfo.Autoclass.newBuilder(); + to.setEnabled(from.getEnabled()); + ifNonNull(from.getToggleTime(), timestampCodec::decode, to::setToggleTime); + return to.build(); + } + + private Bucket.Autoclass autoclassEncode(BucketInfo.Autoclass from) { + Bucket.Autoclass.Builder to = Bucket.Autoclass.newBuilder(); + ifNonNull(from.getEnabled(), to::setEnabled); + ifNonNull(from.getToggleTime(), timestampCodec::encode, to::setToggleTime); + return to.build(); + } + private Bucket.IamConfig iamConfigEncode(BucketInfo.IamConfiguration from) { Bucket.IamConfig.Builder to = Bucket.IamConfig.newBuilder(); to.setUniformBucketLevelAccess(ublaEncode(from)); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java index 658a8f4c0..faa1f7fb7 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -31,6 +32,7 @@ import com.google.cloud.storage.Bucket; import com.google.cloud.storage.BucketFixture; import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.BucketInfo.Autoclass; import com.google.cloud.storage.BucketInfo.CustomPlacementConfig; import com.google.cloud.storage.Cors; import com.google.cloud.storage.HttpMethod; @@ -44,6 +46,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -495,6 +498,40 @@ public void testEnableDisableBucketDefaultEventBasedHold() } } + @Test + public void testCreateBucketWithAutoclass() throws ExecutionException, InterruptedException { + String bucketName = bucketFixture.newBucketName(); + storageFixtureHttp + .getInstance() + .create( + BucketInfo.newBuilder(bucketName) + .setAutoclass(Autoclass.newBuilder().setEnabled(true).build()) + .build()); + try { + Bucket remoteBucket = storageFixture.getInstance().get(bucketName); + + assertNotNull(remoteBucket.getAutoclass()); + assertTrue(remoteBucket.getAutoclass().getEnabled()); + OffsetDateTime time = remoteBucket.getAutoclass().getToggleTime(); + assertNotNull(time); + + remoteBucket + .toBuilder() + .setAutoclass(Autoclass.newBuilder().setEnabled(false).build()) + .build() + .update(); + + remoteBucket = storageFixture.getInstance().get(bucketName); + assertNotNull(remoteBucket.getAutoclass()); + assertFalse(remoteBucket.getAutoclass().getEnabled()); + assertNotNull(remoteBucket.getAutoclass().getToggleTime()); + assertNotEquals(time, remoteBucket.getAutoclass().getToggleTime()); + } finally { + RemoteStorageHelper.forceDelete( + storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS); + } + } + private void unsetRequesterPays() { Bucket remoteBucket = storageFixture diff --git a/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java b/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java new file mode 100644 index 000000000..b413541c8 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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.example.storage.bucket; + +// [START storage_get_autoclass] + +import com.google.cloud.storage.BucketInfo.Autoclass; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; + +public class GetBucketAutoclass { + public static void getBucketAutoclass(String projectId, String bucketName) { + // The ID of your GCP project + // String projectId = "your-project-id"; + + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService(); + Autoclass autoclass = storage.get(bucketName).getAutoclass(); + String status = autoclass.getEnabled() ? "enabled" : "disabled"; + String toggleTime = autoclass.getToggleTime().toString(); + + System.out.println("Autoclass is currently " + status + " for bucket " + bucketName + + " and was last changed at " + toggleTime); + } +} +// [END storage_get_autoclass] diff --git a/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java b/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java new file mode 100644 index 000000000..ec6ea4204 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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.example.storage.bucket; + +// [START storage_set_autoclass] + +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo.Autoclass; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; + +public class SetBucketAutoclass { + public static void setBucketAutoclass(String projectId, String bucketName) { + // The ID of your GCP project + // String projectId = "your-project-id"; + + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + // Whether to set Autoclass to on or off. + // Note: Only update requests that disable qutoclass are currently supported. + // To enable autoclass, you must enable it at bucket creation time. + boolean enabled = false; + + Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService(); + Bucket bucket = storage.get(bucketName); + + bucket.toBuilder() + .setAutoclass(Autoclass.newBuilder() + .setEnabled(enabled) + .build()) + .build() + .update(); + + System.out.println("Autoclass for bucket " + bucketName + " was " + + (enabled ? "enabled." : "disabled.")); + } +} +// [END storage_set_autoclass] diff --git a/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java b/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java new file mode 100644 index 000000000..9ccda14dd --- /dev/null +++ b/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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.example.storage.bucket; + +import static com.google.common.truth.Truth.assertThat; + +import com.example.storage.TestBase; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.BucketInfo.Autoclass; +import com.google.cloud.storage.testing.RemoteStorageHelper; +import org.junit.Assert; +import org.junit.Test; + +public class AutoclassTest extends TestBase { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + + @Test + public void testSetGetBucketAutoclass() { + String autoclassBucket = RemoteStorageHelper.generateBucketName(); + storage.create(BucketInfo.newBuilder(autoclassBucket) + .setAutoclass(Autoclass.newBuilder().setEnabled(true).build()) + .build()); + try { + SetBucketAutoclass.setBucketAutoclass(PROJECT_ID, autoclassBucket); + Autoclass autoclass = storage.get(autoclassBucket).getAutoclass(); + Assert.assertFalse(autoclass.getEnabled()); + + GetBucketAutoclass.getBucketAutoclass(PROJECT_ID, autoclassBucket); + assertThat(stdOut.getCapturedOutputAsUtf8String()) + .contains(autoclass.getToggleTime().toString()); + } finally { + RemoteStorageHelper.forceDelete(storage, autoclassBucket); + } + } +}