diff --git a/sdk/storage/azure-storage-blob-batch/assets.json b/sdk/storage/azure-storage-blob-batch/assets.json index 89bf38f04c2b..309de98d6650 100644 --- a/sdk/storage/azure-storage-blob-batch/assets.json +++ b/sdk/storage/azure-storage-blob-batch/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-blob-batch", - "Tag": "java/storage/azure-storage-blob-batch_1951fdc3fd" + "Tag": "java/storage/azure-storage-blob-batch_45e14073e7" } diff --git a/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BatchApiTests.java b/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BatchApiTests.java index fc73d04fe5c3..e409835fa982 100644 --- a/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BatchApiTests.java +++ b/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BatchApiTests.java @@ -111,9 +111,33 @@ public void setTierAllSucceed() { .upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()); Response response1 = batch.setBlobAccessTier(containerName, blobName1, AccessTier.HOT); - Response response2 = batch.setBlobAccessTier(containerName, blobName2, AccessTier.COOL); + // Response response2 = batch.setBlobAccessTier(containerName, blobName2, AccessTier.COOL); batchClient.submitBatch(batch); + assertEquals(200, response1.getStatusCode()); + // assertEquals(200, response2.getStatusCode()); + } + + @Test + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + public void setBlobAccessTierSmart() { + String containerName = generateContainerName(); + String blobName1 = generateBlobName(); + String blobName2 = generateBlobName(); + BlobBatchClient premiumBatchClient = new BlobBatchClientBuilder(premiumStorageBlobServiceClient).buildClient(); + BlobBatch batch = premiumBatchClient.getBlobBatch(); + BlobContainerClient containerClient = premiumStorageBlobServiceClient.createBlobContainer(containerName); + containerClient.getBlobClient(blobName1) + .getBlockBlobClient() + .upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()); + containerClient.getBlobClient(blobName2) + .getBlockBlobClient() + .upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()); + + Response response1 = batch.setBlobAccessTier(containerName, blobName1, AccessTier.SMART); + Response response2 = batch.setBlobAccessTier(containerName, blobName2, AccessTier.SMART); + premiumBatchClient.submitBatch(batch); + assertEquals(200, response1.getStatusCode()); assertEquals(200, response2.getStatusCode()); } diff --git a/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BlobBatchTestBase.java b/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BlobBatchTestBase.java index 1be0876741f2..41c8511790cc 100644 --- a/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BlobBatchTestBase.java +++ b/sdk/storage/azure-storage-blob-batch/src/test/java/com/azure/storage/blob/batch/BlobBatchTestBase.java @@ -45,6 +45,7 @@ public class BlobBatchTestBase extends TestProxyTestBase { protected BlobServiceClient primaryBlobServiceClient; protected BlobServiceAsyncClient primaryBlobServiceAsyncClient; protected BlobServiceClient versionedBlobServiceClient; + protected BlobServiceClient premiumStorageBlobServiceClient; @Override public void beforeTest() { @@ -64,6 +65,7 @@ public void beforeTest() { primaryBlobServiceClient = getServiceClient(ENVIRONMENT.getPrimaryAccount()); primaryBlobServiceAsyncClient = getServiceAsyncClient(ENVIRONMENT.getPrimaryAccount()); versionedBlobServiceClient = getServiceClient(ENVIRONMENT.getPrimaryAccount()); + premiumStorageBlobServiceClient = getServiceClient(ENVIRONMENT.getPremiumFileAccount()); } /** diff --git a/sdk/storage/azure-storage-blob/assets.json b/sdk/storage/azure-storage-blob/assets.json index d98b4bc847a7..fdbc6cfd11e9 100644 --- a/sdk/storage/azure-storage-blob/assets.json +++ b/sdk/storage/azure-storage-blob/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-blob", - "Tag": "java/storage/azure-storage-blob_4ab10936db" + "Tag": "java/storage/azure-storage-blob_0f8f4263e8" } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/accesshelpers/BlobPropertiesConstructorProxy.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/accesshelpers/BlobPropertiesConstructorProxy.java index d8dd06e9b6ce..48f9b8e09d0d 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/accesshelpers/BlobPropertiesConstructorProxy.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/accesshelpers/BlobPropertiesConstructorProxy.java @@ -30,7 +30,7 @@ public interface BlobPropertiesConstructorAccessor { } /** - * The method called from the static initializer of {@link BlobProperties} to set it's accessor. + * The method called from the static initializer of {@link BlobProperties} to set its accessor. * * @param accessor The {@link BlobProperties} accessor. */ diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobItemPropertiesInternal.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobItemPropertiesInternal.java index 185a2cb3318e..909005607d59 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobItemPropertiesInternal.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobItemPropertiesInternal.java @@ -205,6 +205,12 @@ public final class BlobItemPropertiesInternal implements XmlSerializable { @Generated public static final AccessTier COLD = fromString("Cold"); + /** + * Static value Smart for AccessTier. + */ + @Generated + public static final AccessTier SMART = fromString("Smart"); + /** * Creates a new instance of AccessTier value. * diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/ArchiveStatus.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/ArchiveStatus.java index d4a413e5f7d3..07f0843fd32b 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/ArchiveStatus.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/ArchiveStatus.java @@ -30,6 +30,12 @@ public final class ArchiveStatus extends ExpandableStringEnum { @Generated public static final ArchiveStatus REHYDRATE_PENDING_TO_COLD = fromString("rehydrate-pending-to-cold"); + /** + * Static value rehydrate-pending-to-smart for ArchiveStatus. + */ + @Generated + public static final ArchiveStatus REHYDRATE_PENDING_TO_SMART = fromString("rehydrate-pending-to-smart"); + /** * Creates a new instance of ArchiveStatus value. * diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobItemProperties.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobItemProperties.java index 68a6247e304f..ccd3b89a3f5b 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobItemProperties.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobItemProperties.java @@ -608,6 +608,26 @@ public BlobItemProperties setAccessTierInferred(Boolean accessTierInferred) { return this; } + /** + * Get the smartAccessTier property: The smartAccessTier property. + * + * @return the smartAccessTier value. + */ + public AccessTier getSmartAccessTier() { + return internalProperties.getSmartAccessTier(); + } + + /** + * Set the smartAccessTier property: The smartAccessTier property. + * + * @param smartAccessTier the smartAccessTier value to set. + * @return the BlobItemProperties object itself. + */ + public BlobItemProperties setSmartAccessTier(AccessTier smartAccessTier) { + internalProperties.setSmartAccessTier(smartAccessTier); + return this; + } + /** * Get the archiveStatus property: Possible values include: 'rehydrate-pending-to-hot', * 'rehydrate-pending-to-cool'. diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobProperties.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobProperties.java index b62ecc33861a..fb5a09556b3f 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobProperties.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobProperties.java @@ -23,13 +23,7 @@ public final class BlobProperties { private final BlobPropertiesInternal internalProperties; static { - BlobPropertiesConstructorProxy - .setAccessor(new BlobPropertiesConstructorProxy.BlobPropertiesConstructorAccessor() { - @Override - public BlobProperties create(BlobPropertiesInternal internalProperties) { - return new BlobProperties(internalProperties); - } - }); + BlobPropertiesConstructorProxy.setAccessor(BlobProperties::new); } private BlobProperties(BlobPropertiesInternal internalProperties) { @@ -67,6 +61,7 @@ private BlobProperties(BlobPropertiesInternal internalProperties) { * @param accessTier Access tier of the blob. * @param isAccessTierInferred Flag indicating if the access tier of the blob was inferred from properties of the * blob. + * @param smartAccessTier The inferred smart access tier of the blob. * @param archiveStatus Archive status of the blob. * @param encryptionKeySha256 SHA256 of the customer provided encryption key used to encrypt the blob on the * server. @@ -125,6 +120,7 @@ public BlobProperties(final OffsetDateTime creationTime, final OffsetDateTime la * @param accessTier Access tier of the blob. * @param isAccessTierInferred Flag indicating if the access tier of the blob was inferred from properties of the * blob. + * @param smartAccessTier The inferred smart access tier of the blob. * @param archiveStatus Archive status of the blob. * @param encryptionKeySha256 SHA256 of the customer provided encryption key used to encrypt the blob on the * server. @@ -193,6 +189,7 @@ public BlobProperties(final OffsetDateTime creationTime, final OffsetDateTime la * @param accessTier Access tier of the blob. * @param isAccessTierInferred Flag indicating if the access tier of the blob was inferred from properties of the * blob. + * @param smartAccessTier The inferred smart access tier of the blob. * @param archiveStatus Archive status of the blob. * @param encryptionKeySha256 SHA256 of the customer provided encryption key used to encrypt the blob on the * server. @@ -259,6 +256,7 @@ public BlobProperties(final OffsetDateTime creationTime, final OffsetDateTime la * @param accessTier Access tier of the blob. * @param isAccessTierInferred Flag indicating if the access tier of the blob was inferred from properties of the * blob. + * @param smartAccessTier The inferred smart access tier of the blob. * @param archiveStatus Archive status of the blob. * @param encryptionKeySha256 SHA256 of the customer provided encryption key used to encrypt the blob on the * server. @@ -331,6 +329,7 @@ public BlobProperties(final OffsetDateTime creationTime, final OffsetDateTime la * @param accessTier Access tier of the blob. * @param isAccessTierInferred Flag indicating if the access tier of the blob was inferred from properties of the * blob. + * @param smartAccessTier The inferred smart access tier of the blob. * @param archiveStatus Archive status of the blob. * @param encryptionKeySha256 SHA256 of the customer provided encryption key used to encrypt the blob on the * server. @@ -697,6 +696,16 @@ public Boolean isAccessTierInferred() { return internalProperties.isAccessTierInferred(); } + /** + * Gets the smart access tier of the blob. + * + * @return the tier of the blob. This is only set for Page blobs on a premium storage account or for Block blobs on + * blob storage or general purpose V2 account. + */ + public AccessTier getSmartAccessTier() { + return internalProperties.getSmartAccessTier(); + } + /** * Gets the archive status of the blob. * diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java index fe1b4affa3a5..50a9eb63ef21 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java @@ -3225,4 +3225,18 @@ void containerNameEncodingOnGetBlobUrl() { assertTrue(blobClient.getBlobUrl().contains(expectedEncodedContainerName)); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void uploadStreamAccessTierSmart() { + bc = cc.getBlobClient(generateBlobName()); + InputStream data = new ByteArrayInputStream(getRandomByteArray(Constants.KB)); + + BlobParallelUploadOptions options = new BlobParallelUploadOptions(data).setTier(AccessTier.SMART); + bc.uploadWithResponse(options, null, Context.NONE); + + Response response = bc.getPropertiesWithResponse(null, null, Context.NONE); + assertEquals(AccessTier.SMART, response.getValue().getAccessTier()); + assertNotNull(response.getValue().getSmartAccessTier()); + } + } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java index d80560afb928..0e7bc5476a32 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java @@ -13,6 +13,7 @@ import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.BinaryData; +import com.azure.core.util.Context; import com.azure.core.util.CoreUtils; import com.azure.core.util.FluxUtil; import com.azure.core.util.ProgressListener; @@ -88,8 +89,10 @@ import reactor.test.StepVerifier; import reactor.util.function.Tuple2; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.UncheckedIOException; import java.net.URL; import java.nio.ByteBuffer; @@ -2577,6 +2580,36 @@ public void setTierCold() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void uploadStreamAccessTierSmart() { + + Flux response + = primaryBlobServiceAsyncClient.createBlobContainer(generateContainerName()).flatMapMany(cc -> { + BlockBlobAsyncClient bc = cc.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); + return bc.upload(DATA.getDefaultFlux(), DATA.getDefaultData().remaining()) + .then(bc.setAccessTierWithResponse(AccessTier.SMART, null, null)) + .flatMap(r -> { + HttpHeaders headers = r.getHeaders(); + + assertTrue(r.getStatusCode() == 200 || r.getStatusCode() == 202); + assertNotNull(headers.getValue(X_MS_VERSION)); + assertNotNull(headers.getValue(X_MS_REQUEST_ID)); + return Mono.empty(); + }) + .then(bc.getProperties()) + .flatMap(r -> { + assertEquals(AccessTier.SMART, r.getAccessTier()); + return Mono.empty(); + }) + .thenMany(cc.listBlobs()); + }); + + StepVerifier.create(response) + .assertNext(r -> assertEquals(AccessTier.SMART, r.getProperties().getAccessTier())) + .verifyComplete(); + } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2021-12-02") @Test public void setTierArchiveStatusRehydratePendingToCold() { diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java index 4e0766c4a296..7bb4be87dd5c 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java @@ -3,10 +3,17 @@ package com.azure.storage.blob.specialized; +import com.azure.core.http.rest.Response; import com.azure.core.test.utils.TestUtils; +import com.azure.core.util.Context; +import com.azure.core.util.polling.PollResponse; import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobClientBuilder; import com.azure.storage.blob.BlobServiceVersion; import com.azure.storage.blob.BlobTestBase; +import com.azure.storage.blob.models.AccessTier; +import com.azure.storage.blob.models.BlobCopyInfo; +import com.azure.storage.blob.models.BlobProperties; import com.azure.storage.blob.models.BlobQueryArrowField; import com.azure.storage.blob.models.BlobQueryArrowFieldType; import com.azure.storage.blob.models.BlobQueryArrowSerialization; @@ -18,7 +25,13 @@ import com.azure.storage.blob.models.BlobQuerySerialization; import com.azure.storage.blob.models.BlobRequestConditions; import com.azure.storage.blob.models.BlobStorageException; +import com.azure.storage.blob.models.RehydratePriority; +import com.azure.storage.blob.options.BlobBeginCopyOptions; +import com.azure.storage.blob.options.BlobCopyFromUrlOptions; import com.azure.storage.blob.options.BlobQueryOptions; +import com.azure.storage.blob.options.BlobSetAccessTierOptions; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; @@ -45,9 +58,11 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import static com.azure.storage.blob.models.ArchiveStatus.REHYDRATE_PENDING_TO_SMART; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -595,7 +610,7 @@ public void queryMultipleRecordsWithProgressReceiver() { } long temp = 0; - // Make sure theyre all increasingly bigger + // Make sure they're all increasingly bigger for (long progress : mockReceiver.progressList) { assertTrue(progress >= temp); temp = progress; @@ -608,7 +623,7 @@ public void queryMultipleRecordsWithProgressReceiver() { = new BlobQueryOptions(expression, new ByteArrayOutputStream()).setProgressConsumer(mockReceiver2); bc.queryWithResponse(options2, null, null); - // Make sure theyre all increasingly bigger + // Make sure they're all increasingly bigger for (long progress : mockReceiver2.progressList) { assertTrue(progress >= temp); temp = progress; @@ -812,7 +827,7 @@ public void copyFromURLSourceErrorAndStatusCode() { BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.copyFromUrl(bc.getBlobUrl())); - assertTrue(e.getStatusCode() == 401); + assertEquals(401, e.getStatusCode()); assertTrue(e.getServiceMessage().contains("NoAuthenticationInformation")); assertTrue(e.getServiceMessage() .contains( @@ -820,6 +835,108 @@ public void copyFromURLSourceErrorAndStatusCode() { } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void startCopyFromURLSmartAccessTier() { + BlockBlobClient destBlob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); + + BlobBeginCopyOptions options = new BlobBeginCopyOptions(bc.getBlobUrl()).setTier(AccessTier.SMART); + + PollResponse operation = destBlob.beginCopy(options).waitForCompletion(); + assertTrue(operation.getStatus().isComplete()); + + Response response = destBlob.getPropertiesWithResponse(null, null, Context.NONE); + assertEquals(AccessTier.SMART, response.getValue().getAccessTier()); + assertNotNull(response.getValue().getSmartAccessTier()); + } + + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void copyFromURLSmartAccessTier() { + String srcBlobSas = bc.generateSas(new BlobServiceSasSignatureValues(OffsetDateTime.now().plusHours(1), + new BlobSasPermission().setReadPermission(true))); + bc = new BlobClientBuilder().endpoint(bc.getBlobUrl()).sasToken(srcBlobSas).buildClient(); + BlockBlobClient destBlob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); + + BlobCopyFromUrlOptions options + = new BlobCopyFromUrlOptions(bc.getBlobUrl() + "?" + srcBlobSas).setTier(AccessTier.SMART); + + destBlob.copyFromUrlWithResponse(options, null, Context.NONE); + + Response response = destBlob.getPropertiesWithResponse(null, null, Context.NONE); + assertEquals(AccessTier.SMART, response.getValue().getAccessTier()); + assertNotNull(response.getValue().getSmartAccessTier()); + } + + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void setTierSmart() { + bc.setAccessTier(AccessTier.SMART); + + Response response = bc.getPropertiesWithResponse(null, null, Context.NONE); + assertEquals(AccessTier.SMART, response.getValue().getAccessTier()); + assertNotNull(response.getValue().getSmartAccessTier()); + } + + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void setTierSmartGetBlobs() { + String blobName1 = generateBlobName(); + String blobName2 = generateBlobName(); + BlobClient blob1 = cc.getBlobClient(blobName1); + BlobClient blob2 = cc.getBlobClient(blobName2); + blob1.upload(new ByteArrayInputStream(new byte[0]), 0); + blob2.upload(new ByteArrayInputStream(new byte[0]), 0); + + blob1.setAccessTier(AccessTier.SMART); + blob2.setAccessTier(AccessTier.SMART); + + cc.listBlobs().forEach(blobItem -> { + if (blobItem.getName().equals(blobName1) || blobItem.getName().equals(blobName2)) { + assertEquals(AccessTier.SMART, blobItem.getProperties().getAccessTier()); + assertNotNull(blobItem.getProperties().getSmartAccessTier()); + } + }); + } + + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void setTierSmartRehydrate() { + bc.setAccessTier(AccessTier.ARCHIVE); + + BlobSetAccessTierOptions options + = new BlobSetAccessTierOptions(AccessTier.SMART).setPriority(RehydratePriority.HIGH); + bc.setAccessTierWithResponse(options, null, Context.NONE); + + Response response = bc.getPropertiesWithResponse(null, null, Context.NONE); + assertEquals(REHYDRATE_PENDING_TO_SMART, response.getValue().getArchiveStatus()); + } + + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-02-06") + @Test + public void setTierSmartRehydrateGetBlobs() { + String blobName1 = generateBlobName(); + String blobName2 = generateBlobName(); + BlobClient blob1 = cc.getBlobClient(blobName1); + BlobClient blob2 = cc.getBlobClient(blobName2); + blob1.upload(new ByteArrayInputStream(new byte[0]), 0); + blob2.upload(new ByteArrayInputStream(new byte[0]), 0); + + blob1.setAccessTier(AccessTier.ARCHIVE); + blob2.setAccessTier(AccessTier.ARCHIVE); + + BlobSetAccessTierOptions options + = new BlobSetAccessTierOptions(AccessTier.SMART).setPriority(RehydratePriority.HIGH); + blob1.setAccessTierWithResponse(options, null, Context.NONE); + blob2.setAccessTierWithResponse(options, null, Context.NONE); + + cc.listBlobs().forEach(blobItem -> { + if (blobItem.getName().equals(blobName1) || blobItem.getName().equals(blobName2)) { + assertEquals(REHYDRATE_PENDING_TO_SMART, blobItem.getProperties().getArchiveStatus()); + } + }); + } + static class MockProgressConsumer implements Consumer { List progressList; diff --git a/sdk/storage/azure-storage-blob/swagger/README.md b/sdk/storage/azure-storage-blob/swagger/README.md index a0c217b3c4ef..851c87ce4faa 100644 --- a/sdk/storage/azure-storage-blob/swagger/README.md +++ b/sdk/storage/azure-storage-blob/swagger/README.md @@ -16,7 +16,7 @@ autorest ### Code generation settings ``` yaml use: '@autorest/java@4.1.62' -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/a30ef1ee2e9795f4d77e8c62fad52b33e60d4cb7/specification/storage/data-plane/Microsoft.BlobStorage/stable/2026-04-06/blob.json +input-file: https://raw.githubusercontent.com/nickliu-msft/azure-rest-api-specs/1ee23319226b26175ed9dbb27fc65d24932b9227/specification/storage/data-plane/Microsoft.BlobStorage/stable/2026-06-06/blob.json java: true output-folder: ../ namespace: com.azure.storage.blob diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/Transforms.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/Transforms.java index e1b8bb6fa3bc..6ab7e7cc1aec 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/Transforms.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/Transforms.java @@ -130,6 +130,8 @@ class Transforms { static final HttpHeaderName X_MS_PERMISSIONS = HttpHeaderName.fromString("x-ms-permissions"); static final HttpHeaderName X_MS_CONTINUATION = HttpHeaderName.fromString("x-ms-continuation"); static final HttpHeaderName X_MS_ACL = HttpHeaderName.fromString("x-ms-acl"); + static final HttpHeaderName X_MS_ACCESS_TIER_INFERRED = HttpHeaderName.fromString("x-ms-access-tier-inferred"); + static final HttpHeaderName X_MS_SMART_ACCESS_TIER = HttpHeaderName.fromString("x-ms-smart-access-tier"); static { // https://docs.oracle.com/javase/8/docs/api/java/util/Date.html#getTime-- @@ -353,10 +355,12 @@ static PathProperties toPathProperties(BlobProperties properties, Response r) String group = r.getHeaders().getValue(X_MS_GROUP); String permissions = r.getHeaders().getValue(X_MS_PERMISSIONS); String acl = r.getHeaders().getValue(X_MS_ACL); + Boolean accessTierInferred = properties.isAccessTierInferred(); + AccessTier smartAccessTier = Transforms.toDataLakeAccessTier(properties.getSmartAccessTier()); return AccessorUtility.getPathPropertiesAccessor() .setPathProperties(pathProperties, properties.getEncryptionScope(), encryptionContext, owner, group, - permissions, acl); + permissions, acl, accessTierInferred, smartAccessTier); } } } diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/AccessorUtility.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/AccessorUtility.java index befe9687e7f4..e9cf18bb10b1 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/AccessorUtility.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/AccessorUtility.java @@ -3,6 +3,7 @@ package com.azure.storage.file.datalake.implementation.util; +import com.azure.storage.file.datalake.models.AccessTier; import com.azure.storage.file.datalake.models.FileSystemProperties; import com.azure.storage.file.datalake.models.PathItem; import com.azure.storage.file.datalake.models.PathPermissions; @@ -31,7 +32,8 @@ private AccessorUtility() { */ public interface PathPropertiesAccessor { PathProperties setPathProperties(PathProperties properties, String encryptionScope, String encryptionContext, - String owner, String group, String permissions, String acl); + String owner, String group, String permissions, String acl, Boolean accessTierInferred, + AccessTier smartAccessTier); } /** diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/AccessTier.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/AccessTier.java index b3ab61632b22..7f9772158f75 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/AccessTier.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/AccessTier.java @@ -82,6 +82,11 @@ public final class AccessTier extends ExpandableStringEnum { */ public static final AccessTier ARCHIVE = fromString("Archive"); + /** + * Static value Smart for AccessTier. + */ + public static final AccessTier SMART = fromString("Smart"); + /** * Creates a new instance of {@link AccessTier} with no string value. * diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/PathProperties.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/PathProperties.java index bbfe90b43c33..22b1cd725a5b 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/PathProperties.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/models/PathProperties.java @@ -49,19 +49,23 @@ public class PathProperties { private String group; private String permissions; private List accessControlList; + private Boolean accessTierInferred; + private AccessTier smartAccessTier; static { - AccessorUtility.setPathPropertiesAccessor( - (properties, encryptionScope, encryptionContext, owner, group, permissions, AccessControlList) -> { - properties.encryptionScope = encryptionScope; - properties.encryptionContext = encryptionContext; - properties.owner = owner; - properties.group = group; - properties.permissions = permissions; - properties.accessControlList = PathAccessControlEntry.parseList(AccessControlList); + AccessorUtility.setPathPropertiesAccessor((properties, encryptionScope, encryptionContext, owner, group, + permissions, AccessControlList, accessTierInferred, smartAccessTier) -> { + properties.encryptionScope = encryptionScope; + properties.encryptionContext = encryptionContext; + properties.owner = owner; + properties.group = group; + properties.permissions = permissions; + properties.accessControlList = PathAccessControlEntry.parseList(AccessControlList); + properties.accessTierInferred = accessTierInferred; + properties.smartAccessTier = smartAccessTier; - return properties; - }); + return properties; + }); } /** @@ -391,6 +395,16 @@ public AccessTier getAccessTier() { return accessTier; } + /** + * Gets whether the access tier of the path was inferred by the service. + * + * @return whether the access tier of the path was inferred by the service, or {@code null} when the service does + * not return an inferred value, such as for Smart tier blobs. + */ + public Boolean isAccessTierInferred() { + return accessTierInferred; + } + /** * Gets the archive status of the path. * @@ -418,6 +432,15 @@ public OffsetDateTime getAccessTierChangeTime() { return accessTierChangeTime; } + /** + * Get the underlying tier of a smart tier blob. Only returned if the blob is in Smart tier. + * + * @return the tier of the path. + */ + public AccessTier getSmartAccessTier() { + return smartAccessTier; + } + /** * Gets the metadata associated to this path. * diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DataLakeTestBase.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DataLakeTestBase.java index e24e61c41b6d..60ab7b4373c4 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DataLakeTestBase.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DataLakeTestBase.java @@ -126,6 +126,7 @@ public class DataLakeTestBase extends TestProxyTestBase { protected DataLakeFileSystemAsyncClient dataLakeFileSystemAsyncClient; protected DataLakeServiceClient primaryDataLakeServiceClient; protected DataLakeServiceAsyncClient primaryDataLakeServiceAsyncClient; + protected DataLakeServiceClient premiumDataLakeServiceClient; protected String fileSystemName; protected int entityNo = 0;