Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose timeStorageClassUpdated property of blob's #456

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -410,6 +410,12 @@ public Builder setStorageClass(StorageClass storageClass) {
return this;
}

@Override
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
infoBuilder.setTimeStorageClassUpdated(timeStorageClassUpdated);
return this;
}

@Override
Builder setMetageneration(Long metageneration) {
infoBuilder.setMetageneration(metageneration);
Expand Down
Expand Up @@ -96,6 +96,7 @@ public StorageObject apply(BlobInfo blobInfo) {
private final String contentDisposition;
private final String contentLanguage;
private final StorageClass storageClass;
private final Long timeStorageClassUpdated;
private final Integer componentCount;
private final boolean isDirectory;
private final CustomerEncryption customerEncryption;
Expand Down Expand Up @@ -297,6 +298,16 @@ public Builder setCustomTime(Long customTime) {
/** Sets the blob's storage class. */
public abstract Builder setStorageClass(StorageClass storageClass);

/**
* Sets the modification time of an object's storage class. Once set it can't be unset directly,
* the only way is to rewrite the object with the desired storage class.
*/
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
throw new UnsupportedOperationException(
"Override setTimeStorageClassUpdated with your own implementation,"
+ " or use com.google.cloud.storage.Blob.");
}

/** Sets the blob's user provided metadata. */
public abstract Builder setMetadata(Map<String, String> metadata);

Expand Down Expand Up @@ -356,6 +367,7 @@ static final class BuilderImpl extends Builder {
private Boolean isDirectory;
private CustomerEncryption customerEncryption;
private StorageClass storageClass;
private Long timeStorageClassUpdated;
private String kmsKeyName;
private Boolean eventBasedHold;
private Boolean temporaryHold;
Expand Down Expand Up @@ -391,6 +403,7 @@ static final class BuilderImpl extends Builder {
createTime = blobInfo.createTime;
isDirectory = blobInfo.isDirectory;
storageClass = blobInfo.storageClass;
timeStorageClassUpdated = blobInfo.timeStorageClassUpdated;
kmsKeyName = blobInfo.kmsKeyName;
eventBasedHold = blobInfo.eventBasedHold;
temporaryHold = blobInfo.temporaryHold;
Expand Down Expand Up @@ -564,6 +577,12 @@ public Builder setStorageClass(StorageClass storageClass) {
return this;
}

@Override
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
this.timeStorageClassUpdated = timeStorageClassUpdated;
return this;
}

@Override
Builder setMetageneration(Long metageneration) {
this.metageneration = metageneration;
Expand Down Expand Up @@ -657,6 +676,7 @@ public BlobInfo build() {
createTime = builder.createTime;
isDirectory = firstNonNull(builder.isDirectory, Boolean.FALSE);
storageClass = builder.storageClass;
timeStorageClassUpdated = builder.timeStorageClassUpdated;
kmsKeyName = builder.kmsKeyName;
eventBasedHold = builder.eventBasedHold;
temporaryHold = builder.temporaryHold;
Expand Down Expand Up @@ -917,6 +937,14 @@ public StorageClass getStorageClass() {
return storageClass;
}

/**
* Returns the time that the object's storage class was last changed or the time of the object
* creation.
*/
public Long getTimeStorageClassUpdated() {
return timeStorageClassUpdated;
}

/** Returns the Cloud KMS key used to encrypt the blob, if any. */
public String getKmsKeyName() {
return kmsKeyName;
Expand Down Expand Up @@ -1049,6 +1077,9 @@ public ObjectAccessControl apply(Acl acl) {
if (storageClass != null) {
storageObject.setStorageClass(storageClass.toString());
}
if (timeStorageClassUpdated != null) {
storageObject.setTimeStorageClassUpdated(new DateTime(timeStorageClassUpdated));
}

Map<String, String> pbMetadata = metadata;
if (metadata != null && !Data.isNull(metadata)) {
Expand Down Expand Up @@ -1193,6 +1224,9 @@ public Acl apply(ObjectAccessControl objectAccessControl) {
if (storageObject.getStorageClass() != null) {
builder.setStorageClass(StorageClass.valueOf(storageObject.getStorageClass()));
}
if (storageObject.getTimeStorageClassUpdated() != null) {
builder.setTimeStorageClassUpdated(storageObject.getTimeStorageClassUpdated().getValue());
}
if (storageObject.getKmsKeyName() != null) {
builder.setKmsKeyName(storageObject.getKmsKeyName());
}
Expand Down
Expand Up @@ -154,7 +154,8 @@ enum BlobField implements FieldSelector {
TEMPORARY_HOLD("temporaryHold"),
RETENTION_EXPIRATION_TIME("retentionExpirationTime"),
UPDATED("updated"),
CUSTOM_TIME("customTime");
CUSTOM_TIME("customTime"),
TIME_STORAGE_CLASS_UPDATED("timeStorageClassUpdated");

static final List<? extends FieldSelector> REQUIRED_FIELDS = ImmutableList.of(BUCKET, NAME);

Expand Down
Expand Up @@ -75,6 +75,7 @@ public class BlobInfoTest {
private static final String KMS_KEY_NAME =
"projects/p/locations/kr-loc/keyRings/kr/cryptoKeys/key";
private static final StorageClass STORAGE_CLASS = StorageClass.COLDLINE;
private static final Long TIME_STORAGE_CLASS_UPDATED = CREATE_TIME;
private static final Boolean EVENT_BASED_HOLD = true;
private static final Boolean TEMPORARY_HOLD = true;
private static final Long RETENTION_EXPIRATION_TIME = 10L;
Expand Down Expand Up @@ -104,6 +105,7 @@ public class BlobInfoTest {
.setCreateTime(CREATE_TIME)
.setCustomTime(CUSTOM_TIME)
.setStorageClass(STORAGE_CLASS)
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
.setKmsKeyName(KMS_KEY_NAME)
.setEventBasedHold(EVENT_BASED_HOLD)
.setTemporaryHold(TEMPORARY_HOLD)
Expand Down Expand Up @@ -199,6 +201,7 @@ public void testBuilder() {
assertEquals(CREATE_TIME, BLOB_INFO.getCreateTime());
assertEquals(CUSTOM_TIME, BLOB_INFO.getCustomTime());
assertEquals(STORAGE_CLASS, BLOB_INFO.getStorageClass());
assertEquals(TIME_STORAGE_CLASS_UPDATED, BLOB_INFO.getTimeStorageClassUpdated());
assertEquals(KMS_KEY_NAME, BLOB_INFO.getKmsKeyName());
assertEquals(EVENT_BASED_HOLD, BLOB_INFO.getEventBasedHold());
assertEquals(TEMPORARY_HOLD, BLOB_INFO.getTemporaryHold());
Expand Down Expand Up @@ -264,6 +267,7 @@ private void compareBlobs(BlobInfo expected, BlobInfo value) {
assertEquals(expected.getCustomTime(), value.getCustomTime());
assertEquals(expected.getUpdateTime(), value.getUpdateTime());
assertEquals(expected.getStorageClass(), value.getStorageClass());
assertEquals(expected.getTimeStorageClassUpdated(), value.getTimeStorageClassUpdated());
assertEquals(expected.getKmsKeyName(), value.getKmsKeyName());
assertEquals(expected.getEventBasedHold(), value.getEventBasedHold());
assertEquals(expected.getTemporaryHold(), value.getTemporaryHold());
Expand Down Expand Up @@ -319,6 +323,7 @@ public void testToPbAndFromPb() {
assertEquals(0L, (long) blobInfo.getSize());
assertNull(blobInfo.getUpdateTime());
assertNull(blobInfo.getStorageClass());
assertNull(blobInfo.getTimeStorageClassUpdated());
assertNull(blobInfo.getKmsKeyName());
assertNull(blobInfo.getEventBasedHold());
assertNull(blobInfo.getTemporaryHold());
Expand Down
Expand Up @@ -92,6 +92,8 @@ public class BlobTest {
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 StorageClass STORAGE_CLASS = StorageClass.COLDLINE;
private static final Long TIME_STORAGE_CLASS_UPDATED = CREATE_TIME;
private static final String ENCRYPTION_ALGORITHM = "AES256";
private static final String KEY_SHA256 = "keySha";
private static final BlobInfo.CustomerEncryption CUSTOMER_ENCRYPTION =
Expand Down Expand Up @@ -124,6 +126,8 @@ public class BlobTest {
.setUpdateTime(UPDATE_TIME)
.setCreateTime(CREATE_TIME)
.setCustomTime(CUSTOM_TIME)
.setStorageClass(STORAGE_CLASS)
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
.setCustomerEncryption(CUSTOMER_ENCRYPTION)
.setKmsKeyName(KMS_KEY_NAME)
.setEventBasedHold(EVENT_BASED_HOLD)
Expand Down Expand Up @@ -513,6 +517,8 @@ public void testBuilder() {
.setCrc32c(CRC32)
.setCreateTime(CREATE_TIME)
.setCustomTime(CUSTOM_TIME)
.setStorageClass(STORAGE_CLASS)
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
.setCustomerEncryption(CUSTOMER_ENCRYPTION)
.setKmsKeyName(KMS_KEY_NAME)
.setEventBasedHold(EVENT_BASED_HOLD)
Expand Down Expand Up @@ -543,6 +549,8 @@ public void testBuilder() {
assertEquals(CRC32_HEX_STRING, blob.getCrc32cToHexString());
assertEquals(CREATE_TIME, blob.getCreateTime());
assertEquals(CUSTOM_TIME, blob.getCustomTime());
assertEquals(STORAGE_CLASS, blob.getStorageClass());
assertEquals(TIME_STORAGE_CLASS_UPDATED, blob.getTimeStorageClassUpdated());
assertEquals(CUSTOMER_ENCRYPTION, blob.getCustomerEncryption());
assertEquals(KMS_KEY_NAME, blob.getKmsKeyName());
assertEquals(EVENT_BASED_HOLD, blob.getEventBasedHold());
Expand Down Expand Up @@ -576,6 +584,8 @@ public void testBuilder() {
assertNull(blob.getCrc32c());
assertNull(blob.getCrc32cToHexString());
assertNull(blob.getCreateTime());
assertNull(blob.getStorageClass());
assertNull(blob.getTimeStorageClassUpdated());
assertNull(blob.getCustomerEncryption());
assertNull(blob.getKmsKeyName());
assertNull(blob.getEventBasedHold());
Expand Down
Expand Up @@ -3567,4 +3567,42 @@ public void testBucketUpdateTime() throws ExecutionException, InterruptedExcepti
RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
}
}

@Test
public void testBlobTimeStorageClassUpdated() {
String blobName = "test-blob-with-storage-class";
StorageClass storageClass = StorageClass.COLDLINE;
BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setStorageClass(storageClass).build();
Blob remoteBlob = storage.create(blob);
assertThat(remoteBlob).isNotNull();
assertThat(remoteBlob.getBucket()).isEqualTo(blob.getBucket());
assertThat(remoteBlob.getName()).isEqualTo(blob.getName());
assertThat(remoteBlob.getCreateTime()).isNotNull();
assertThat(remoteBlob.getUpdateTime()).isEqualTo(remoteBlob.getCreateTime());
assertThat(remoteBlob.getTimeStorageClassUpdated()).isEqualTo(remoteBlob.getCreateTime());

// We can't change an object's storage class directly, the only way is to rewrite the object
// with the desired storage class.
BlobId blobId = BlobId.of(BUCKET, blobName);
Storage.CopyRequest request =
Storage.CopyRequest.newBuilder()
.setSource(blobId)
.setTarget(BlobInfo.newBuilder(blobId).setStorageClass(StorageClass.STANDARD).build())
.build();
Blob updatedBlob1 = storage.copy(request).getResult();
assertThat(updatedBlob1.getTimeStorageClassUpdated()).isNotNull();
assertThat(updatedBlob1.getCreateTime()).isGreaterThan(remoteBlob.getCreateTime());
assertThat(updatedBlob1.getUpdateTime()).isGreaterThan(remoteBlob.getCreateTime());
assertThat(updatedBlob1.getTimeStorageClassUpdated())
.isGreaterThan(remoteBlob.getTimeStorageClassUpdated());

// Updates the other properties of the blob's to check the difference between blob updateTime
// and timeStorageClassUpdated.
Blob updatedBlob2 = updatedBlob1.toBuilder().setContentType(CONTENT_TYPE).build().update();
assertThat(updatedBlob2.getUpdateTime())
.isGreaterThan(updatedBlob2.getTimeStorageClassUpdated());
assertThat(updatedBlob2.getTimeStorageClassUpdated())
.isEqualTo(updatedBlob1.getTimeStorageClassUpdated());
assertThat(updatedBlob2.delete()).isTrue();
}
}