Skip to content

Commit 57853ec

Browse files
authored
feat: expose timeStorageClassUpdated property of blob's (#456)
* feat: expose timeStorageClassUpdated field of blob * fix: package imports * feat: update javadoc * feat: add exception to avoid breaking change
1 parent db358c8 commit 57853ec

File tree

6 files changed

+95
-1
lines changed

6 files changed

+95
-1
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,12 @@ public Builder setStorageClass(StorageClass storageClass) {
410410
return this;
411411
}
412412

413+
@Override
414+
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
415+
infoBuilder.setTimeStorageClassUpdated(timeStorageClassUpdated);
416+
return this;
417+
}
418+
413419
@Override
414420
Builder setMetageneration(Long metageneration) {
415421
infoBuilder.setMetageneration(metageneration);

google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public StorageObject apply(BlobInfo blobInfo) {
9696
private final String contentDisposition;
9797
private final String contentLanguage;
9898
private final StorageClass storageClass;
99+
private final Long timeStorageClassUpdated;
99100
private final Integer componentCount;
100101
private final boolean isDirectory;
101102
private final CustomerEncryption customerEncryption;
@@ -297,6 +298,16 @@ public Builder setCustomTime(Long customTime) {
297298
/** Sets the blob's storage class. */
298299
public abstract Builder setStorageClass(StorageClass storageClass);
299300

301+
/**
302+
* Sets the modification time of an object's storage class. Once set it can't be unset directly,
303+
* the only way is to rewrite the object with the desired storage class.
304+
*/
305+
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
306+
throw new UnsupportedOperationException(
307+
"Override setTimeStorageClassUpdated with your own implementation,"
308+
+ " or use com.google.cloud.storage.Blob.");
309+
}
310+
300311
/** Sets the blob's user provided metadata. */
301312
public abstract Builder setMetadata(Map<String, String> metadata);
302313

@@ -356,6 +367,7 @@ static final class BuilderImpl extends Builder {
356367
private Boolean isDirectory;
357368
private CustomerEncryption customerEncryption;
358369
private StorageClass storageClass;
370+
private Long timeStorageClassUpdated;
359371
private String kmsKeyName;
360372
private Boolean eventBasedHold;
361373
private Boolean temporaryHold;
@@ -391,6 +403,7 @@ static final class BuilderImpl extends Builder {
391403
createTime = blobInfo.createTime;
392404
isDirectory = blobInfo.isDirectory;
393405
storageClass = blobInfo.storageClass;
406+
timeStorageClassUpdated = blobInfo.timeStorageClassUpdated;
394407
kmsKeyName = blobInfo.kmsKeyName;
395408
eventBasedHold = blobInfo.eventBasedHold;
396409
temporaryHold = blobInfo.temporaryHold;
@@ -564,6 +577,12 @@ public Builder setStorageClass(StorageClass storageClass) {
564577
return this;
565578
}
566579

580+
@Override
581+
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
582+
this.timeStorageClassUpdated = timeStorageClassUpdated;
583+
return this;
584+
}
585+
567586
@Override
568587
Builder setMetageneration(Long metageneration) {
569588
this.metageneration = metageneration;
@@ -657,6 +676,7 @@ public BlobInfo build() {
657676
createTime = builder.createTime;
658677
isDirectory = firstNonNull(builder.isDirectory, Boolean.FALSE);
659678
storageClass = builder.storageClass;
679+
timeStorageClassUpdated = builder.timeStorageClassUpdated;
660680
kmsKeyName = builder.kmsKeyName;
661681
eventBasedHold = builder.eventBasedHold;
662682
temporaryHold = builder.temporaryHold;
@@ -917,6 +937,14 @@ public StorageClass getStorageClass() {
917937
return storageClass;
918938
}
919939

940+
/**
941+
* Returns the time that the object's storage class was last changed or the time of the object
942+
* creation.
943+
*/
944+
public Long getTimeStorageClassUpdated() {
945+
return timeStorageClassUpdated;
946+
}
947+
920948
/** Returns the Cloud KMS key used to encrypt the blob, if any. */
921949
public String getKmsKeyName() {
922950
return kmsKeyName;
@@ -1049,6 +1077,9 @@ public ObjectAccessControl apply(Acl acl) {
10491077
if (storageClass != null) {
10501078
storageObject.setStorageClass(storageClass.toString());
10511079
}
1080+
if (timeStorageClassUpdated != null) {
1081+
storageObject.setTimeStorageClassUpdated(new DateTime(timeStorageClassUpdated));
1082+
}
10521083

10531084
Map<String, String> pbMetadata = metadata;
10541085
if (metadata != null && !Data.isNull(metadata)) {
@@ -1193,6 +1224,9 @@ public Acl apply(ObjectAccessControl objectAccessControl) {
11931224
if (storageObject.getStorageClass() != null) {
11941225
builder.setStorageClass(StorageClass.valueOf(storageObject.getStorageClass()));
11951226
}
1227+
if (storageObject.getTimeStorageClassUpdated() != null) {
1228+
builder.setTimeStorageClassUpdated(storageObject.getTimeStorageClassUpdated().getValue());
1229+
}
11961230
if (storageObject.getKmsKeyName() != null) {
11971231
builder.setKmsKeyName(storageObject.getKmsKeyName());
11981232
}

google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ enum BlobField implements FieldSelector {
154154
TEMPORARY_HOLD("temporaryHold"),
155155
RETENTION_EXPIRATION_TIME("retentionExpirationTime"),
156156
UPDATED("updated"),
157-
CUSTOM_TIME("customTime");
157+
CUSTOM_TIME("customTime"),
158+
TIME_STORAGE_CLASS_UPDATED("timeStorageClassUpdated");
158159

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

google-cloud-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public class BlobInfoTest {
7575
private static final String KMS_KEY_NAME =
7676
"projects/p/locations/kr-loc/keyRings/kr/cryptoKeys/key";
7777
private static final StorageClass STORAGE_CLASS = StorageClass.COLDLINE;
78+
private static final Long TIME_STORAGE_CLASS_UPDATED = CREATE_TIME;
7879
private static final Boolean EVENT_BASED_HOLD = true;
7980
private static final Boolean TEMPORARY_HOLD = true;
8081
private static final Long RETENTION_EXPIRATION_TIME = 10L;
@@ -104,6 +105,7 @@ public class BlobInfoTest {
104105
.setCreateTime(CREATE_TIME)
105106
.setCustomTime(CUSTOM_TIME)
106107
.setStorageClass(STORAGE_CLASS)
108+
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
107109
.setKmsKeyName(KMS_KEY_NAME)
108110
.setEventBasedHold(EVENT_BASED_HOLD)
109111
.setTemporaryHold(TEMPORARY_HOLD)
@@ -199,6 +201,7 @@ public void testBuilder() {
199201
assertEquals(CREATE_TIME, BLOB_INFO.getCreateTime());
200202
assertEquals(CUSTOM_TIME, BLOB_INFO.getCustomTime());
201203
assertEquals(STORAGE_CLASS, BLOB_INFO.getStorageClass());
204+
assertEquals(TIME_STORAGE_CLASS_UPDATED, BLOB_INFO.getTimeStorageClassUpdated());
202205
assertEquals(KMS_KEY_NAME, BLOB_INFO.getKmsKeyName());
203206
assertEquals(EVENT_BASED_HOLD, BLOB_INFO.getEventBasedHold());
204207
assertEquals(TEMPORARY_HOLD, BLOB_INFO.getTemporaryHold());
@@ -264,6 +267,7 @@ private void compareBlobs(BlobInfo expected, BlobInfo value) {
264267
assertEquals(expected.getCustomTime(), value.getCustomTime());
265268
assertEquals(expected.getUpdateTime(), value.getUpdateTime());
266269
assertEquals(expected.getStorageClass(), value.getStorageClass());
270+
assertEquals(expected.getTimeStorageClassUpdated(), value.getTimeStorageClassUpdated());
267271
assertEquals(expected.getKmsKeyName(), value.getKmsKeyName());
268272
assertEquals(expected.getEventBasedHold(), value.getEventBasedHold());
269273
assertEquals(expected.getTemporaryHold(), value.getTemporaryHold());
@@ -319,6 +323,7 @@ public void testToPbAndFromPb() {
319323
assertEquals(0L, (long) blobInfo.getSize());
320324
assertNull(blobInfo.getUpdateTime());
321325
assertNull(blobInfo.getStorageClass());
326+
assertNull(blobInfo.getTimeStorageClassUpdated());
322327
assertNull(blobInfo.getKmsKeyName());
323328
assertNull(blobInfo.getEventBasedHold());
324329
assertNull(blobInfo.getTemporaryHold());

google-cloud-storage/src/test/java/com/google/cloud/storage/BlobTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ public class BlobTest {
9292
private static final Long UPDATE_TIME = DELETE_TIME - 1L;
9393
private static final Long CREATE_TIME = UPDATE_TIME - 1L;
9494
private static final Long CUSTOM_TIME = CREATE_TIME - 1L;
95+
private static final StorageClass STORAGE_CLASS = StorageClass.COLDLINE;
96+
private static final Long TIME_STORAGE_CLASS_UPDATED = CREATE_TIME;
9597
private static final String ENCRYPTION_ALGORITHM = "AES256";
9698
private static final String KEY_SHA256 = "keySha";
9799
private static final BlobInfo.CustomerEncryption CUSTOMER_ENCRYPTION =
@@ -124,6 +126,8 @@ public class BlobTest {
124126
.setUpdateTime(UPDATE_TIME)
125127
.setCreateTime(CREATE_TIME)
126128
.setCustomTime(CUSTOM_TIME)
129+
.setStorageClass(STORAGE_CLASS)
130+
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
127131
.setCustomerEncryption(CUSTOMER_ENCRYPTION)
128132
.setKmsKeyName(KMS_KEY_NAME)
129133
.setEventBasedHold(EVENT_BASED_HOLD)
@@ -513,6 +517,8 @@ public void testBuilder() {
513517
.setCrc32c(CRC32)
514518
.setCreateTime(CREATE_TIME)
515519
.setCustomTime(CUSTOM_TIME)
520+
.setStorageClass(STORAGE_CLASS)
521+
.setTimeStorageClassUpdated(TIME_STORAGE_CLASS_UPDATED)
516522
.setCustomerEncryption(CUSTOMER_ENCRYPTION)
517523
.setKmsKeyName(KMS_KEY_NAME)
518524
.setEventBasedHold(EVENT_BASED_HOLD)
@@ -543,6 +549,8 @@ public void testBuilder() {
543549
assertEquals(CRC32_HEX_STRING, blob.getCrc32cToHexString());
544550
assertEquals(CREATE_TIME, blob.getCreateTime());
545551
assertEquals(CUSTOM_TIME, blob.getCustomTime());
552+
assertEquals(STORAGE_CLASS, blob.getStorageClass());
553+
assertEquals(TIME_STORAGE_CLASS_UPDATED, blob.getTimeStorageClassUpdated());
546554
assertEquals(CUSTOMER_ENCRYPTION, blob.getCustomerEncryption());
547555
assertEquals(KMS_KEY_NAME, blob.getKmsKeyName());
548556
assertEquals(EVENT_BASED_HOLD, blob.getEventBasedHold());
@@ -576,6 +584,8 @@ public void testBuilder() {
576584
assertNull(blob.getCrc32c());
577585
assertNull(blob.getCrc32cToHexString());
578586
assertNull(blob.getCreateTime());
587+
assertNull(blob.getStorageClass());
588+
assertNull(blob.getTimeStorageClassUpdated());
579589
assertNull(blob.getCustomerEncryption());
580590
assertNull(blob.getKmsKeyName());
581591
assertNull(blob.getEventBasedHold());

google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3567,4 +3567,42 @@ public void testBucketUpdateTime() throws ExecutionException, InterruptedExcepti
35673567
RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
35683568
}
35693569
}
3570+
3571+
@Test
3572+
public void testBlobTimeStorageClassUpdated() {
3573+
String blobName = "test-blob-with-storage-class";
3574+
StorageClass storageClass = StorageClass.COLDLINE;
3575+
BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setStorageClass(storageClass).build();
3576+
Blob remoteBlob = storage.create(blob);
3577+
assertThat(remoteBlob).isNotNull();
3578+
assertThat(remoteBlob.getBucket()).isEqualTo(blob.getBucket());
3579+
assertThat(remoteBlob.getName()).isEqualTo(blob.getName());
3580+
assertThat(remoteBlob.getCreateTime()).isNotNull();
3581+
assertThat(remoteBlob.getUpdateTime()).isEqualTo(remoteBlob.getCreateTime());
3582+
assertThat(remoteBlob.getTimeStorageClassUpdated()).isEqualTo(remoteBlob.getCreateTime());
3583+
3584+
// We can't change an object's storage class directly, the only way is to rewrite the object
3585+
// with the desired storage class.
3586+
BlobId blobId = BlobId.of(BUCKET, blobName);
3587+
Storage.CopyRequest request =
3588+
Storage.CopyRequest.newBuilder()
3589+
.setSource(blobId)
3590+
.setTarget(BlobInfo.newBuilder(blobId).setStorageClass(StorageClass.STANDARD).build())
3591+
.build();
3592+
Blob updatedBlob1 = storage.copy(request).getResult();
3593+
assertThat(updatedBlob1.getTimeStorageClassUpdated()).isNotNull();
3594+
assertThat(updatedBlob1.getCreateTime()).isGreaterThan(remoteBlob.getCreateTime());
3595+
assertThat(updatedBlob1.getUpdateTime()).isGreaterThan(remoteBlob.getCreateTime());
3596+
assertThat(updatedBlob1.getTimeStorageClassUpdated())
3597+
.isGreaterThan(remoteBlob.getTimeStorageClassUpdated());
3598+
3599+
// Updates the other properties of the blob's to check the difference between blob updateTime
3600+
// and timeStorageClassUpdated.
3601+
Blob updatedBlob2 = updatedBlob1.toBuilder().setContentType(CONTENT_TYPE).build().update();
3602+
assertThat(updatedBlob2.getUpdateTime())
3603+
.isGreaterThan(updatedBlob2.getTimeStorageClassUpdated());
3604+
assertThat(updatedBlob2.getTimeStorageClassUpdated())
3605+
.isEqualTo(updatedBlob1.getTimeStorageClassUpdated());
3606+
assertThat(updatedBlob2.delete()).isTrue();
3607+
}
35703608
}

0 commit comments

Comments
 (0)