Skip to content
Draft
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob-batch/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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_469f26eff9"
"Tag": "java/storage/azure-storage-blob-batch_37ee945799"
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public final class BlobBatch {
* @throws UnsupportedOperationException If this batch has already added an operation of another type.
*/
public Response<Void> deleteBlob(String containerName, String blobName) {
return deleteBlobHelper(containerName + "/" + Utility.urlEncode(Utility.urlDecode(blobName)), null, null);
return deleteBlobHelper(Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName), null, null);
}

/**
Expand All @@ -158,7 +158,7 @@ public Response<Void> deleteBlob(String containerName, String blobName) {
*/
public Response<Void> deleteBlob(String containerName, String blobName, DeleteSnapshotsOptionType deleteOptions,
BlobRequestConditions blobRequestConditions) {
return deleteBlobHelper(containerName + "/" + Utility.urlEncode(Utility.urlDecode(blobName)), deleteOptions,
return deleteBlobHelper(Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName), deleteOptions,
blobRequestConditions);
}

Expand Down Expand Up @@ -234,7 +234,7 @@ private Response<Void> deleteBlobHelper(String urlPath, DeleteSnapshotsOptionTyp
* @throws UnsupportedOperationException If this batch has already added an operation of another type.
*/
public Response<Void> setBlobAccessTier(String containerName, String blobName, AccessTier accessTier) {
return setBlobAccessTierHelper(containerName + "/" + Utility.urlEncode(Utility.urlDecode(blobName)), accessTier,
return setBlobAccessTierHelper(Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName), accessTier,
null, null, null);
}

Expand All @@ -260,7 +260,7 @@ public Response<Void> setBlobAccessTier(String containerName, String blobName, A
*/
public Response<Void> setBlobAccessTier(String containerName, String blobName, AccessTier accessTier,
String leaseId) {
return setBlobAccessTierHelper(containerName + "/" + Utility.urlEncode(Utility.urlDecode(blobName)), accessTier,
return setBlobAccessTierHelper(Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName), accessTier,
null, leaseId, null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public BlobBatchSetBlobAccessTierOptions(String blobUrl, AccessTier tier) {
* Creates a new instance of {@link BlobBatchSetBlobAccessTierOptions}.
*
* @param containerName Name of the container to set access tier.
* @param blobName Name of the blob to set access tier. Blob names must be encoded to UTF-8.
* @param blobName Name of the blob to set access tier.
* @param tier {@link AccessTier} to set on each blob.
* @throws NullPointerException If {@code containerName}, {@code blobName}, or {@code tier} is null.
*/
Expand All @@ -51,8 +51,8 @@ public BlobBatchSetBlobAccessTierOptions(String containerName, String blobName,
StorageImplUtils.assertNotNull("blobName", blobName);
StorageImplUtils.assertNotNull("tier", tier);
this.blobUrlParts = BlobUrlParts.parse("https://account.blob.core.windows.net")
.setContainerName(containerName)
.setBlobName(blobName);
.setContainerName(Utility.urlEncode(containerName))
.setBlobName(Utility.urlEncode(blobName));
this.tier = tier;
}

Expand All @@ -66,18 +66,18 @@ public String getBlobUrl() {
}

/**
* Gets the container name of the blob to set its access tier.
* Gets the unencoded container name of the blob to set its access tier.
*
* @return Container of the blob to set its access tier.
* @return Unencoded name of the container of the blob to set its access tier.
*/
public String getBlobContainerName() {
return blobUrlParts.getBlobContainerName();
}

/**
* Gets the name of the blob to set its access tier.
* Gets the unencoded name of the blob to set its access tier.
*
* @return Name of the blob to set its access tier.
* @return Unencoded name of the blob to set its access tier.
*/
public String getBlobName() {
return blobUrlParts.getBlobName();
Expand All @@ -89,7 +89,8 @@ public String getBlobName() {
* @return Identifier of the blob to set its access tier.
*/
public String getBlobIdentifier() {
String basePath = blobUrlParts.getBlobContainerName() + "/" + blobUrlParts.getBlobName();
String basePath = Utility.urlEncode(blobUrlParts.getBlobContainerName()) + "/"
+ Utility.urlEncode(blobUrlParts.getBlobName());
String snapshot = blobUrlParts.getSnapshot();
String versionId = blobUrlParts.getVersionId();
if (snapshot != null && versionId != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.azure.storage.blob.specialized.BlobClientBase;
import com.azure.storage.blob.specialized.BlockBlobClient;
import com.azure.storage.blob.specialized.PageBlobClient;
import com.azure.storage.common.Utility;
import com.azure.storage.common.sas.AccountSasPermission;
import com.azure.storage.common.sas.AccountSasResourceType;
import com.azure.storage.common.sas.AccountSasService;
Expand Down Expand Up @@ -794,4 +795,140 @@ public void submitBatchWithContainerSasCredentialsError() {
= assertThrows(BlobBatchStorageException.class, () -> batchClient.submitBatch(batch));
assertEquals(2, getIterableSize(ex.getBatchExceptions()));
}

// Tests container name encoding for BlobBatch.deleteBlob. Container names with special characters are not supported
// by the service, however, the names should still be encoded.
@Test
public void deleteBlobContainerNameEncoding() {
String containerName = generateContainerName() + "enc!";
String blobName = generateBlobName();

BlobBatch batch = batchClient.getBlobBatch();
Response<Void> response = batch.deleteBlob(containerName, blobName);

assertThrows(BlobBatchStorageException.class, () -> batchClient.submitBatch(batch));
BlobStorageException temp = assertThrows(BlobStorageException.class, response::getRequest);

assertTrue(temp.getResponse().getRequest().getUrl().toString().contains(Utility.urlEncode(containerName)));
}

// Tests blob name encoding for BlobBatch.deleteBlob.
@Test
public void deleteBlobNameEncoding() {
String containerName = generateContainerName();
String blobName = generateBlobName() + "enc!";
BlobContainerClient containerClient = primaryBlobServiceClient.createBlobContainer(containerName);
containerClient.getBlobClient(blobName).getPageBlobClient().create(0);

BlobBatch batch = batchClient.getBlobBatch();
Response<Void> response = batch.deleteBlob(containerName, blobName);
batchClient.submitBatch(batch);

assertEquals(202, response.getStatusCode());
}

// Tests container name encoding for BlobBatch.setBlobAccessTier. Container names with special characters are not supported
// by the service, however, the names should still be encoded.
@Test
public void setTierContainerNameEncoding() {
String containerName = generateContainerName() + "enc!";
String blobName = generateBlobName();

BlobBatch batch = batchClient.getBlobBatch();
Response<Void> response = batch.setBlobAccessTier(containerName, blobName, AccessTier.HOT);

assertThrows(BlobBatchStorageException.class, () -> batchClient.submitBatch(batch));
BlobStorageException temp = assertThrows(BlobStorageException.class, response::getRequest);

assertTrue(temp.getResponse().getRequest().getUrl().toString().contains(Utility.urlEncode(containerName)));
}

// Tests blob name encoding for BlobBatch.setBlobAccessTier
@Test
public void setTierBlobNameEncoding() {
String containerName = generateContainerName();
String blobName = generateBlobName() + "enc!";
BlobContainerClient containerClient = primaryBlobServiceClient.createBlobContainer(containerName);
containerClient.getBlobClient(blobName).getBlockBlobClient().upload(DATA.getDefaultBinaryData());

BlobBatch batch = batchClient.getBlobBatch();
Response<Void> response = batch.setBlobAccessTier(containerName, blobName, AccessTier.HOT);
batchClient.submitBatch(batch);

assertEquals(200, response.getStatusCode());
}

// Tests container name encoding for BlobBatchSetBlobAccessTierOptions constructor. Container names with special characters are not supported
// by the service, however, the names should still be encoded.
@Test
public void setTierContainerNameEncodingOptionsConstructor() {
String containerName = generateContainerName() + "enc!";
String blobName = generateBlobName();

BlobBatch batch = batchClient.getBlobBatch();
BlobBatchSetBlobAccessTierOptions options
= new BlobBatchSetBlobAccessTierOptions(containerName, blobName, AccessTier.HOT);
Response<Void> response = batch.setBlobAccessTier(options);

assertThrows(BlobBatchStorageException.class, () -> batchClient.submitBatch(batch));
BlobStorageException temp = assertThrows(BlobStorageException.class, response::getRequest);

assertTrue(temp.getResponse().getRequest().getUrl().toString().contains(Utility.urlEncode(containerName)));
}

//Tests blob name encoding for BlobBatchSetBlobAccessTierOptions constructor
@Test
public void setTierBlobNameEncodingOptionsConstructor() {
String containerName = generateContainerName();
String blobName = generateBlobName() + "enc!";
BlobContainerClient containerClient = primaryBlobServiceClient.createBlobContainer(containerName);
containerClient.getBlobClient(blobName).getBlockBlobClient().upload(DATA.getDefaultBinaryData());

BlobBatch batch = batchClient.getBlobBatch();
BlobBatchSetBlobAccessTierOptions options
= new BlobBatchSetBlobAccessTierOptions(containerName, blobName, AccessTier.HOT);
Response<Void> response = batch.setBlobAccessTier(options);
batchClient.submitBatch(batch);

assertEquals(200, response.getStatusCode());
String identifier = options.getBlobIdentifier();
assertTrue(identifier.contains(Utility.urlEncode(blobName)));
}

// Tests getters return unencoded names (constructor with separate names)
@Test
public void getBlobNameAndContainerNameOptionsConstructor() {
String containerName = generateContainerName() + "enc!";
String blobName = generateBlobName() + "enc!";

BlobBatchSetBlobAccessTierOptions options
= new BlobBatchSetBlobAccessTierOptions(containerName, blobName, AccessTier.HOT);

assertEquals(containerName, options.getBlobContainerName());
assertEquals(blobName, options.getBlobName());

String identifier = options.getBlobIdentifier();
assertTrue(identifier.contains(Utility.urlEncode(blobName)));
assertTrue(identifier.contains(Utility.urlEncode(containerName)));
}

// Tests getters return unencoded names (constructor with full blob URL)
@Test
public void getBlobNameAndContainerNameUrlConstructor() {
String containerName = generateContainerName() + "enc!";
String blobName = generateBlobName() + "enc!";
BlockBlobClient blockBlobClient = primaryBlobServiceClient.getBlobContainerClient(containerName)
.getBlobClient(blobName)
.getBlockBlobClient();

BlobBatchSetBlobAccessTierOptions options
= new BlobBatchSetBlobAccessTierOptions(blockBlobClient.getBlobUrl(), AccessTier.HOT);

assertEquals(containerName, options.getBlobContainerName());
assertEquals(blobName, options.getBlobName());

String identifier = options.getBlobIdentifier();
assertTrue(identifier.contains(Utility.urlEncode(blobName)));
assertTrue(identifier.contains(Utility.urlEncode(containerName)));
}
}
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/storage/azure-storage-blob",
"Tag": "java/storage/azure-storage-blob_16534d98da"
"Tag": "java/storage/azure-storage-blob_c018337a13"
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import com.azure.storage.blob.options.FindBlobsOptions;
import com.azure.storage.blob.sas.BlobServiceSasSignatureValues;
import com.azure.storage.common.StorageSharedKeyCredential;
import com.azure.storage.common.Utility;
import com.azure.storage.common.implementation.SasImplUtils;
import com.azure.storage.common.implementation.StorageImplUtils;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -220,7 +221,7 @@ public String getAccountUrl() {
* @return the URL.
*/
public String getBlobContainerUrl() {
return azureBlobStorage.getUrl() + "/" + containerName;
return azureBlobStorage.getUrl() + "/" + Utility.urlEncode(containerName);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.azure.storage.blob.options.FindBlobsOptions;
import com.azure.storage.blob.sas.BlobServiceSasSignatureValues;
import com.azure.storage.common.StorageSharedKeyCredential;
import com.azure.storage.common.Utility;
import com.azure.storage.common.implementation.SasImplUtils;
import com.azure.storage.common.implementation.StorageImplUtils;

Expand Down Expand Up @@ -232,7 +233,7 @@ public String getAccountUrl() {
* @return the URL.
*/
public String getBlobContainerUrl() {
return azureBlobStorage.getUrl() + "/" + containerName;
return azureBlobStorage.getUrl() + "/" + Utility.urlEncode(containerName);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,28 @@ public BlobUrlParts setHost(String host) {
}

/**
* Gets the container name that will be used as part of the URL path.
* Gets the decoded container name that will be used as part of the URL path.
*
* @return the container name.
*/
public String getBlobContainerName() {
return containerName;
return (containerName == null) ? null : Utility.urlDecode(containerName);
}

/**
* Sets the container name that will be used as part of the URL path.
*
* @param containerName The container nme.
* @param containerName The container name.
* @return the updated BlobUrlParts object.
*/
public BlobUrlParts setContainerName(String containerName) {
this.containerName = containerName;
//decodes and encodes to ensure containerName is always stored in encoded format
this.containerName = Utility.urlEncode(Utility.urlDecode(containerName));
return this;
}

/**
* Decodes and gets the blob name that will be used as part of the URL path.
* Gets the decoded blob name that will be used as part of the URL path.
*
* @return the decoded blob name.
*/
Expand All @@ -146,6 +147,7 @@ public String getBlobName() {
* @return the updated BlobUrlParts object.
*/
public BlobUrlParts setBlobName(String blobName) {
//decodes and encodes to ensure blobName is always stored in encoded format
this.blobName = Utility.urlEncode(Utility.urlDecode(blobName));
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ public String getAccountUrl() {
* @return the URL.
*/
public String getBlobUrl() {
String blobUrl = azureBlobStorage.getUrl() + "/" + containerName + "/" + Utility.urlEncode(blobName);
String blobUrl
= azureBlobStorage.getUrl() + "/" + Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName);
if (this.isSnapshot()) {
blobUrl = Utility.appendQueryParameter(blobUrl, "snapshot", getSnapshotId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ public String getAccountUrl() {
* @return the URL.
*/
public String getBlobUrl() {
String blobUrl = azureBlobStorage.getUrl() + "/" + containerName + "/" + Utility.urlEncode(blobName);
String blobUrl
= azureBlobStorage.getUrl() + "/" + Utility.urlEncode(containerName) + "/" + Utility.urlEncode(blobName);
if (this.isSnapshot()) {
blobUrl = Utility.appendQueryParameter(blobUrl, "snapshot", getSnapshotId());
}
Expand Down
Loading
Loading