Skip to content

Commit

Permalink
feat: migrate model classes to use java.time instead of Long (#1388)
Browse files Browse the repository at this point in the history
* Add Codec<DateTime, OffsetDateTime> for apiary conversion
* Add Utils#RFC_3339_DATE_TIME_FORMATTER
* Add Utils#millisOffsetDateTimeCodec to provide compatibility with existing Long centric apis
* Update BucketInfo and BucketInfo.Builder to prefer java.time types instead of Long
* Update ApiaryConversions for BucketInfo to use new java.time types

Update the following files to now be OffestDateTime instead of Long
* BlobInfo#customTime
* BlobInfo#deleteTime
* BlobInfo#updateTime
* BlobInfo#createTime
* BlobInfo#timeStorageClassUpdated
* BlobInfo#retentionExpirationTime
* BucketInfo#retentionEffectiveTime
* IamConfiguration#uniformBucketLevelAccessLockedTime
  • Loading branch information
BenWhitehead authored May 13, 2022
1 parent 1600f5e commit a2e9b88
Show file tree
Hide file tree
Showing 8 changed files with 503 additions and 74 deletions.
4 changes: 4 additions & 0 deletions google-cloud-storage/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
</dependency>
<!--
We're not using this directly, however there appears to be some resolution
issues when we don't list this dependency. Specifically, the check to ensure the flattened
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.google.cloud.storage.Utils.ifNonNull;
import static com.google.cloud.storage.Utils.lift;
import static com.google.cloud.storage.Utils.toImmutableListOf;
import static com.google.common.base.MoreObjects.firstNonNull;

import com.google.api.client.util.Data;
Expand Down Expand Up @@ -62,15 +63,22 @@
import com.google.cloud.storage.HmacKey.HmacKeyState;
import com.google.cloud.storage.NotificationInfo.EventType;
import com.google.cloud.storage.NotificationInfo.PayloadFormat;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@InternalApi
final class ApiaryConversions {
Expand Down Expand Up @@ -113,6 +121,29 @@ final class ApiaryConversions {
private final Codec<NotificationInfo, com.google.api.services.storage.model.Notification>
notificationInfoCodec = Codec.of(this::notificationEncode, this::notificationDecode);

@VisibleForTesting
final Codec<DateTime, OffsetDateTime> dateTimeCodec =
Codec.of(
dt -> {
long milli = dt.getValue();
int timeZoneShiftMinutes = dt.getTimeZoneShift();

Duration timeZoneShift = Duration.of(timeZoneShiftMinutes, ChronoUnit.MINUTES);

int hours = Math.toIntExact(timeZoneShift.toHours());
int minutes =
Math.toIntExact(
timeZoneShift.minusHours(timeZoneShift.toHours()).getSeconds() / 60);
ZoneOffset offset = ZoneOffset.ofHoursMinutes(hours, minutes);

return Instant.ofEpochMilli(milli).atOffset(offset);
},
odt -> {
ZoneOffset offset = odt.getOffset();
int i = Math.toIntExact(TimeUnit.SECONDS.toMinutes(offset.getTotalSeconds()));
return new DateTime(odt.toInstant().toEpochMilli(), i);
});

private ApiaryConversions() {}

Codec<Entity, String> entity() {
Expand Down Expand Up @@ -182,21 +213,27 @@ Codec<NotificationInfo, com.google.api.services.storage.model.Notification> noti

private StorageObject blobInfoEncode(BlobInfo from) {
StorageObject to = blobIdEncode(from.getBlobId());
ifNonNull(from.getAcl(), Utils.toImmutableListOf(objectAcl()::encode), to::setAcl);
ifNonNull(from.getDeleteTime(), DateTime::new, to::setTimeDeleted);
ifNonNull(from.getUpdateTime(), DateTime::new, to::setUpdated);
ifNonNull(from.getCreateTime(), DateTime::new, to::setTimeCreated);
ifNonNull(from.getCustomTime(), DateTime::new, to::setCustomTime);
ifNonNull(from.getAcl(), toImmutableListOf(objectAcl()::encode), to::setAcl);
ifNonNull(from.getDeleteTimeOffsetDateTime(), dateTimeCodec::decode, to::setTimeDeleted);
ifNonNull(from.getUpdateTimeOffsetDateTime(), dateTimeCodec::decode, to::setUpdated);
ifNonNull(from.getCreateTimeOffsetDateTime(), dateTimeCodec::decode, to::setTimeCreated);
ifNonNull(from.getCustomTimeOffsetDateTime(), dateTimeCodec::decode, to::setCustomTime);
ifNonNull(from.getSize(), BigInteger::valueOf, to::setSize);
ifNonNull(
from.getOwner(),
lift(this::entityEncode).andThen(o -> new Owner().setEntity(o)),
to::setOwner);
ifNonNull(from.getStorageClass(), StorageClass::toString, to::setStorageClass);
ifNonNull(from.getTimeStorageClassUpdated(), DateTime::new, to::setTimeStorageClassUpdated);
ifNonNull(
from.getTimeStorageClassUpdatedOffsetDateTime(),
dateTimeCodec::decode,
to::setTimeStorageClassUpdated);
ifNonNull(
from.getCustomerEncryption(), this::customerEncryptionEncode, to::setCustomerEncryption);
ifNonNull(from.getRetentionExpirationTime(), DateTime::new, to::setRetentionExpirationTime);
ifNonNull(
from.getRetentionExpirationTimeOffsetDateTime(),
dateTimeCodec::decode,
to::setRetentionExpirationTime);
to.setKmsKeyName(from.getKmsKeyName());
to.setEventBasedHold(from.getEventBasedHold());
to.setTemporaryHold(from.getTemporaryHold());
Expand Down Expand Up @@ -241,26 +278,26 @@ private BlobInfo blobInfoDecode(StorageObject from) {
ifNonNull(from.getId(), to::setGeneratedId);
ifNonNull(from.getSelfLink(), to::setSelfLink);
ifNonNull(from.getMetadata(), to::setMetadata);
ifNonNull(from.getTimeDeleted(), DateTime::getValue, to::setDeleteTime);
ifNonNull(from.getUpdated(), DateTime::getValue, to::setUpdateTime);
ifNonNull(from.getTimeCreated(), DateTime::getValue, to::setCreateTime);
ifNonNull(from.getCustomTime(), DateTime::getValue, to::setCustomTime);
ifNonNull(from.getTimeDeleted(), dateTimeCodec::encode, to::setDeleteTime);
ifNonNull(from.getUpdated(), dateTimeCodec::encode, to::setUpdateTime);
ifNonNull(from.getTimeCreated(), dateTimeCodec::encode, to::setCreateTime);
ifNonNull(from.getCustomTime(), dateTimeCodec::encode, to::setCustomTime);
ifNonNull(from.getSize(), BigInteger::longValue, to::setSize);
ifNonNull(from.getOwner(), lift(Owner::getEntity).andThen(this::entityDecode), to::setOwner);
ifNonNull(from.getAcl(), Utils.toImmutableListOf(objectAcl()::decode), to::setAcl);
ifNonNull(from.getAcl(), toImmutableListOf(objectAcl()::decode), to::setAcl);
if (from.containsKey("isDirectory")) {
to.setIsDirectory(Boolean.TRUE);
}
ifNonNull(
from.getCustomerEncryption(), this::customerEncryptionDecode, to::setCustomerEncryption);
ifNonNull(from.getStorageClass(), StorageClass::valueOf, to::setStorageClass);
ifNonNull(
from.getTimeStorageClassUpdated(), DateTime::getValue, to::setTimeStorageClassUpdated);
from.getTimeStorageClassUpdated(), dateTimeCodec::encode, to::setTimeStorageClassUpdated);
ifNonNull(from.getKmsKeyName(), to::setKmsKeyName);
ifNonNull(from.getEventBasedHold(), to::setEventBasedHold);
ifNonNull(from.getTemporaryHold(), to::setTemporaryHold);
ifNonNull(
from.getRetentionExpirationTime(), DateTime::getValue, to::setRetentionExpirationTime);
from.getRetentionExpirationTime(), dateTimeCodec::encode, to::setRetentionExpirationTime);
return to.build();
}

Expand Down Expand Up @@ -288,13 +325,11 @@ private CustomerEncryption customerEncryptionDecode(StorageObject.CustomerEncryp

private Bucket bucketInfoEncode(BucketInfo from) {
Bucket to = new Bucket();
ifNonNull(from.getAcl(), Utils.toImmutableListOf(bucketAcl()::encode), to::setAcl);
ifNonNull(from.getCors(), Utils.toImmutableListOf(cors()::encode), to::setCors);
ifNonNull(from.getCreateTime(), DateTime::new, to::setTimeCreated);
ifNonNull(from.getAcl(), toImmutableListOf(bucketAcl()::encode), to::setAcl);
ifNonNull(from.getCors(), toImmutableListOf(cors()::encode), to::setCors);
ifNonNull(from.getCreateTimeOffsetDateTime(), dateTimeCodec::decode, to::setTimeCreated);
ifNonNull(
from.getDefaultAcl(),
Utils.toImmutableListOf(objectAcl()::encode),
to::setDefaultObjectAcl);
from.getDefaultAcl(), toImmutableListOf(objectAcl()::encode), to::setDefaultObjectAcl);
ifNonNull(from.getLocation(), to::setLocation);
ifNonNull(from.getLocationType(), to::setLocationType);
ifNonNull(from.getMetageneration(), to::setMetageneration);
Expand All @@ -304,7 +339,7 @@ private Bucket bucketInfoEncode(BucketInfo from) {
to::setOwner);
ifNonNull(from.getRpo(), Rpo::toString, to::setRpo);
ifNonNull(from.getStorageClass(), StorageClass::toString, to::setStorageClass);
ifNonNull(from.getUpdateTime(), DateTime::new, to::setUpdated);
ifNonNull(from.getUpdateTimeOffsetDateTime(), dateTimeCodec::decode, to::setUpdated);
ifNonNull(from.versioningEnabled(), b -> new Versioning().setEnabled(b), to::setVersioning);
to.setEtag(from.getEtag());
to.setId(from.getGeneratedId());
Expand Down Expand Up @@ -362,12 +397,11 @@ private Bucket bucketInfoEncode(BucketInfo from) {
} else {
Bucket.RetentionPolicy retentionPolicy = new Bucket.RetentionPolicy();
retentionPolicy.setRetentionPeriod(from.getRetentionPeriod());
if (from.getRetentionEffectiveTime() != null) {
retentionPolicy.setEffectiveTime(new DateTime(from.getRetentionEffectiveTime()));
}
if (from.retentionPolicyIsLocked() != null) {
retentionPolicy.setIsLocked(from.retentionPolicyIsLocked());
}
ifNonNull(
from.getRetentionEffectiveTimeOffsetDateTime(),
dateTimeCodec::decode,
retentionPolicy::setEffectiveTime);
ifNonNull(from.retentionPolicyIsLocked(), retentionPolicy::setIsLocked);
to.setRetentionPolicy(retentionPolicy);
}
}
Expand All @@ -379,12 +413,10 @@ private Bucket bucketInfoEncode(BucketInfo from) {
@SuppressWarnings("deprecation")
private BucketInfo bucketInfoDecode(com.google.api.services.storage.model.Bucket from) {
BucketInfo.Builder to = new BuilderImpl(from.getName());
ifNonNull(from.getAcl(), Utils.toImmutableListOf(bucketAcl()::decode), to::setAcl);
ifNonNull(from.getCors(), Utils.toImmutableListOf(cors()::decode), to::setCors);
ifNonNull(from.getAcl(), toImmutableListOf(bucketAcl()::decode), to::setAcl);
ifNonNull(from.getCors(), toImmutableListOf(cors()::decode), to::setCors);
ifNonNull(
from.getDefaultObjectAcl(),
Utils.toImmutableListOf(objectAcl()::decode),
to::setDefaultAcl);
from.getDefaultObjectAcl(), toImmutableListOf(objectAcl()::decode), to::setDefaultAcl);
ifNonNull(from.getEtag(), to::setEtag);
ifNonNull(from.getId(), to::setGeneratedId);
ifNonNull(from.getLocation(), to::setLocation);
Expand All @@ -395,19 +427,19 @@ private BucketInfo bucketInfoDecode(com.google.api.services.storage.model.Bucket
ifNonNull(from.getRpo(), Rpo::valueOf, to::setRpo);
ifNonNull(from.getSelfLink(), to::setSelfLink);
ifNonNull(from.getStorageClass(), StorageClass::valueOf, to::setStorageClass);
ifNonNull(from.getTimeCreated(), DateTime::getValue, to::setCreateTime);
ifNonNull(from.getUpdated(), DateTime::getValue, to::setUpdateTime);
ifNonNull(from.getTimeCreated(), dateTimeCodec::encode, to::setCreateTime);
ifNonNull(from.getUpdated(), dateTimeCodec::encode, to::setUpdateTime);
ifNonNull(from.getVersioning(), Versioning::getEnabled, to::setVersioningEnabled);
ifNonNull(from.getWebsite(), Website::getMainPageSuffix, to::setIndexPage);
ifNonNull(from.getWebsite(), Website::getNotFoundPage, to::setNotFoundPage);
ifNonNull(
from.getLifecycle(),
lift(Lifecycle::getRule).andThen(Utils.toImmutableListOf(lifecycleRule()::decode)),
lift(Lifecycle::getRule).andThen(toImmutableListOf(lifecycleRule()::decode)),
to::setLifecycleRules);
// preserve mapping to deprecated property
ifNonNull(
from.getLifecycle(),
lift(Lifecycle::getRule).andThen(Utils.toImmutableListOf(deleteRule()::decode)),
lift(Lifecycle::getRule).andThen(toImmutableListOf(deleteRule()::decode)),
to::setDeleteRules);
ifNonNull(from.getDefaultEventBasedHold(), to::setDefaultEventBasedHold);
ifNonNull(from.getLabels(), to::setLabels);
Expand All @@ -421,7 +453,7 @@ private BucketInfo bucketInfoDecode(com.google.api.services.storage.model.Bucket

RetentionPolicy retentionPolicy = from.getRetentionPolicy();
if (retentionPolicy != null && retentionPolicy.getEffectiveTime() != null) {
to.setRetentionEffectiveTime(retentionPolicy.getEffectiveTime().getValue());
to.setRetentionEffectiveTime(dateTimeCodec.encode(retentionPolicy.getEffectiveTime()));
}
ifNonNull(retentionPolicy, RetentionPolicy::getIsLocked, to::setRetentionPolicyIsLocked);
ifNonNull(retentionPolicy, RetentionPolicy::getRetentionPeriod, to::setRetentionPeriod);
Expand Down Expand Up @@ -485,7 +517,8 @@ private IamConfiguration iamConfigDecode(Bucket.IamConfiguration from) {

IamConfiguration.Builder to =
IamConfiguration.newBuilder().setIsUniformBucketLevelAccessEnabled(ubla.getEnabled());
ifNonNull(ubla.getLockedTime(), DateTime::getValue, to::setUniformBucketLevelAccessLockedTime);
ifNonNull(
ubla.getLockedTime(), dateTimeCodec::encode, to::setUniformBucketLevelAccessLockedTime);
ifNonNull(
from.getPublicAccessPrevention(),
PublicAccessPrevention::parse,
Expand All @@ -496,7 +529,10 @@ private IamConfiguration iamConfigDecode(Bucket.IamConfiguration from) {
private UniformBucketLevelAccess ublaEncode(IamConfiguration from) {
UniformBucketLevelAccess to = new UniformBucketLevelAccess();
to.setEnabled(from.isUniformBucketLevelAccessEnabled());
ifNonNull(from.getUniformBucketLevelAccessLockedTime(), DateTime::new, to::setLockedTime);
ifNonNull(
from.getUniformBucketLevelAccessLockedTimeOffsetDateTime(),
dateTimeCodec::decode,
to::setLockedTime);
return to;
}

Expand Down Expand Up @@ -524,7 +560,7 @@ private Condition ruleConditionEncode(LifecycleCondition from) {
from.getCustomTimeBefore(), this::truncateToDateWithNoTzDrift, to::setCustomTimeBefore);
ifNonNull(
from.getMatchesStorageClass(),
Utils.toImmutableListOf(Object::toString),
toImmutableListOf(Object::toString),
to::setMatchesStorageClass);
return to;
}
Expand Down Expand Up @@ -575,7 +611,7 @@ private LifecycleRule lifecycleRuleDecode(Rule from) {
.setDaysSinceCustomTime(condition.getDaysSinceCustomTime());
ifNonNull(
condition.getMatchesStorageClass(),
Utils.toImmutableListOf(StorageClass::valueOf),
toImmutableListOf(StorageClass::valueOf),
conditionBuilder::setMatchesStorageClass);

return new LifecycleRule(lifecycleAction, conditionBuilder.build());
Expand Down Expand Up @@ -604,8 +640,8 @@ private Bucket.Cors corsEncode(Cors from) {
Bucket.Cors to = new Bucket.Cors();
to.setMaxAgeSeconds(from.getMaxAgeSeconds());
to.setResponseHeader(from.getResponseHeaders());
ifNonNull(from.getMethods(), Utils.toImmutableListOf(Object::toString), to::setMethod);
ifNonNull(from.getOrigins(), Utils.toImmutableListOf(Object::toString), to::setOrigin);
ifNonNull(from.getMethods(), toImmutableListOf(Object::toString), to::setMethod);
ifNonNull(from.getOrigins(), toImmutableListOf(Object::toString), to::setOrigin);
return to;
}

Expand All @@ -619,7 +655,7 @@ private Cors corsDecode(Bucket.Cors from) {
.map(HttpMethod::valueOf)
.collect(ImmutableList.toImmutableList()),
to::setMethods);
ifNonNull(from.getOrigin(), Utils.toImmutableListOf(Origin::of), to::setOrigins);
ifNonNull(from.getOrigin(), toImmutableListOf(Origin::of), to::setOrigins);
to.setResponseHeaders(from.getResponseHeader());
return to.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.google.cloud.storage;

import static java.util.Objects.requireNonNull;

import com.google.cloud.storage.Conversions.Codec;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* A collection of utilities that only exist to enable backward compatibility.
*
* <p>In general, the expectation is that any references to this class only come from @Deprecated
* things.
*/
final class BackwardCompatibilityUtils {

@SuppressWarnings("RedundantTypeArguments")
// the <Long, OffsetDateTime> doesn't auto carry all the way through like intellij thinks it
// would.
static final Codec<@Nullable Long, @Nullable OffsetDateTime> millisOffsetDateTimeCodec =
Codec.<Long, OffsetDateTime>of(
m ->
Instant.ofEpochMilli(requireNonNull(m, "m must be non null"))
.atOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())),
odt -> requireNonNull(odt, "odt must be non null").toInstant().toEpochMilli())
.nullable();

private BackwardCompatibilityUtils() {}
}
Loading

0 comments on commit a2e9b88

Please sign in to comment.