diff --git a/sdk/storage/storage-blob/review/storage-blob.api.md b/sdk/storage/storage-blob/review/storage-blob.api.md index a9e4e14a92d8..fe0da7c71172 100644 --- a/sdk/storage/storage-blob/review/storage-blob.api.md +++ b/sdk/storage/storage-blob/review/storage-blob.api.md @@ -2706,10 +2706,7 @@ export class StorageSharedKeyCredentialPolicy extends CredentialPolicy { export type SyncCopyStatusType = 'success'; // @public -export interface Tags { - // (undocumented) - [propertyName: string]: string; -} +export type Tags = Record; // @public export interface UserDelegationKey { diff --git a/sdk/storage/storage-blob/src/BlobSASSignatureValues.ts b/sdk/storage/storage-blob/src/BlobSASSignatureValues.ts index 25b7cfd0cafb..b3eff14da424 100644 --- a/sdk/storage/storage-blob/src/BlobSASSignatureValues.ts +++ b/sdk/storage/storage-blob/src/BlobSASSignatureValues.ts @@ -475,18 +475,22 @@ function generateBlobSASQueryParameters20181109( ); } - if (blobSASSignatureValues.versionId) { + const version = blobSASSignatureValues.version ? blobSASSignatureValues.version : SERVICE_VERSION; + let resource: string = "c"; + let verifiedPermissions: string | undefined; + + if (blobSASSignatureValues.versionId && version < "2019-10-10") { throw RangeError("'version' must be >= '2019-10-10' when provided 'versionId'."); } - if (blobSASSignatureValues.permissions && blobSASSignatureValues.permissions.tag) { + if ( + blobSASSignatureValues.permissions && + blobSASSignatureValues.permissions.tag && + version < "2019-12-12" + ) { throw RangeError("'version' must be >= '2019-12-12' when provided 't' permission."); } - const version = blobSASSignatureValues.version ? blobSASSignatureValues.version : SERVICE_VERSION; - let resource: string = "c"; - let verifiedPermissions: string | undefined; - if (blobSASSignatureValues.blobName === undefined && blobSASSignatureValues.snapshotTime) { throw RangeError("Must provide 'blobName' when provided 'snapshotTime'."); } diff --git a/sdk/storage/storage-blob/src/BlobServiceClient.ts b/sdk/storage/storage-blob/src/BlobServiceClient.ts index e969d543f92e..fff24db33399 100644 --- a/sdk/storage/storage-blob/src/BlobServiceClient.ts +++ b/sdk/storage/storage-blob/src/BlobServiceClient.ts @@ -834,6 +834,8 @@ export class BlobServiceClient extends StorageClient { * * .byPage() returns an async iterable iterator to list the blobs in pages. * + * @see https://docs.microsoft.com/en-us/rest/api/storageservices/get-blob-service-properties + * * Example using `for await` syntax: * * ```js diff --git a/sdk/storage/storage-blob/src/models.ts b/sdk/storage/storage-blob/src/models.ts index c6199b36d0cf..dec8da7e987b 100644 --- a/sdk/storage/storage-blob/src/models.ts +++ b/sdk/storage/storage-blob/src/models.ts @@ -13,13 +13,8 @@ import { EncryptionAlgorithmAES25 } from "./utils/constants"; /** * Blob tags. - * - * @export - * @interface Tags */ -export interface Tags { - [propertyName: string]: string; -} +export type Tags = Record; /** * A map of name-value pairs to associate with the resource. diff --git a/sdk/storage/storage-blob/test/blobclient.spec.ts b/sdk/storage/storage-blob/test/blobclient.spec.ts index 0c4f34cf1f14..f0ebdf745a7a 100644 --- a/sdk/storage/storage-blob/test/blobclient.spec.ts +++ b/sdk/storage/storage-blob/test/blobclient.spec.ts @@ -9,7 +9,7 @@ import { getBSU, getSASConnectionStringFromEnvironment, recorderEnvSetup, - isBlobVersioningDisabled, + isBlobVersioningDisabled } from "./utils"; import { record, delay } from "@azure/test-utils-recorder"; import { @@ -52,7 +52,7 @@ describe("BlobClient", () => { } }); - it.only("Set blob tags should work", async () => { + it("Set blob tags should work", async () => { const tags = { tag1: "val1", tag2: "val2" @@ -77,7 +77,7 @@ describe("BlobClient", () => { assert.deepStrictEqual(segment.value.segment.blobItems[0].tags, tags); }); - it.only("Get blob tags should work with a snapshot", async () => { + it("Get blob tags should work with a snapshot", async () => { const tags = { tag1: "val1", tag2: "val2" @@ -91,7 +91,7 @@ describe("BlobClient", () => { assert.deepStrictEqual(response.tags, tags); }); - it.only("Create block blob blob should work with tags", async () => { + it("Create block blob blob should work with tags", async () => { await blockBlobClient.delete(); const tags = { @@ -104,7 +104,7 @@ describe("BlobClient", () => { assert.deepStrictEqual(response.tags, tags); }); - it.only("Create append blob should work with tags", async () => { + it("Create append blob should work with tags", async () => { await blockBlobClient.delete(); const tags = { @@ -119,7 +119,7 @@ describe("BlobClient", () => { assert.deepStrictEqual(response.tags, tags); }); - it.only("Create page blob should work with tags", async () => { + it("Create page blob should work with tags", async () => { await blockBlobClient.delete(); const tags = { @@ -336,7 +336,7 @@ describe("BlobClient", () => { const iter = containerClient .listBlobsFlat({ includeDeleted: true, - includeVersions: true, // Need this when blob versioning is turned on. + includeVersions: true // Need this when blob versioning is turned on. }) .byPage({ maxPageSize: 1 }); @@ -375,7 +375,10 @@ describe("BlobClient", () => { ); if (isBlobVersioningDisabled()) { - assert.ok(result.segment.blobItems![0].deleted, "Expect that the blob is marked for deletion"); + assert.ok( + result.segment.blobItems![0].deleted, + "Expect that the blob is marked for deletion" + ); } await blobClient.undelete(); @@ -383,7 +386,7 @@ describe("BlobClient", () => { const iter2 = containerClient .listBlobsFlat({ includeDeleted: true, - includeVersions: true, // Need this when blob versioning is turned on. + includeVersions: true // Need this when blob versioning is turned on. }) .byPage(); diff --git a/sdk/storage/storage-blob/test/blobserviceclient.spec.ts b/sdk/storage/storage-blob/test/blobserviceclient.spec.ts index 1e5f00b2150d..d0a541f2b1a3 100644 --- a/sdk/storage/storage-blob/test/blobserviceclient.spec.ts +++ b/sdk/storage/storage-blob/test/blobserviceclient.spec.ts @@ -469,7 +469,7 @@ describe("BlobServiceClient", () => { assert.notDeepStrictEqual(response.signedExpiresOn, undefined); }); - it.only("Find blob by tags should work", async () => { + it("Find blob by tags should work", async () => { const blobServiceClient = getBSU(); const containerName = recorder.getUniqueName("container1"); diff --git a/sdk/storage/storage-blob/test/browser/highlevel.browser.spec.ts b/sdk/storage/storage-blob/test/browser/highlevel.browser.spec.ts index 0c255eedc278..9c14a4736fa9 100644 --- a/sdk/storage/storage-blob/test/browser/highlevel.browser.spec.ts +++ b/sdk/storage/storage-blob/test/browser/highlevel.browser.spec.ts @@ -154,7 +154,7 @@ describe("Highlevel", () => { assert.equal(uploadedString, downloadedString); }); - it.only("uploadBrowserDataToBlockBlob should work with tags", async () => { + it("uploadBrowserDataToBlockBlob should work with tags", async () => { recorder.skip("browser", "Temp file - recorder doesn't support saving the file"); const tags = { diff --git a/sdk/storage/storage-blob/test/node/highlevel.node.spec.ts b/sdk/storage/storage-blob/test/node/highlevel.node.spec.ts index 1d67094f2666..4dcec8d15235 100644 --- a/sdk/storage/storage-blob/test/node/highlevel.node.spec.ts +++ b/sdk/storage/storage-blob/test/node/highlevel.node.spec.ts @@ -4,7 +4,12 @@ import * as path from "path"; import { PassThrough } from "stream"; import { AbortController } from "@azure/abort-controller"; -import { createRandomLocalFile, getBSU, recorderEnvSetup, isBlobVersioningDisabled } from "../utils"; +import { + createRandomLocalFile, + getBSU, + recorderEnvSetup, + isBlobVersioningDisabled +} from "../utils"; import { RetriableReadableStreamOptions } from "../../src/utils/RetriableReadableStream"; import { record, Recorder } from "@azure/test-utils-recorder"; import { ContainerClient, BlobClient, BlockBlobClient, BlobServiceClient } from "../../src"; @@ -28,7 +33,7 @@ describe("Highlevel", () => { let recorder: Recorder; let blobServiceClient: BlobServiceClient; - beforeEach(async function () { + beforeEach(async function() { recorder = record(this, recorderEnvSetup); blobServiceClient = getBSU(); containerName = recorder.getUniqueName("container"); @@ -39,14 +44,14 @@ describe("Highlevel", () => { blockBlobClient = blobClient.getBlockBlobClient(); }); - afterEach(async function () { + afterEach(async function() { if (!this.currentTest?.isPending()) { await containerClient.delete(); recorder.stop(); } }); - before(async function () { + before(async function() { recorder = record(this, recorderEnvSetup); if (!fs.existsSync(tempFolderPath)) { fs.mkdirSync(tempFolderPath); @@ -58,7 +63,7 @@ describe("Highlevel", () => { recorder.stop(); }); - after(async function () { + after(async function() { recorder = record(this, recorderEnvSetup); fs.unlinkSync(tempFileLarge); fs.unlinkSync(tempFileSmall); @@ -67,7 +72,7 @@ describe("Highlevel", () => { it("put blob with maximum size", async () => { recorder.skip("node", "Temp file - recorder doesn't support saving the file"); - const MB = 1024 * 1024 + const MB = 1024 * 1024; const maxPutBlobSizeLimitInMB = 5000; const tempFile = await createRandomLocalFile(tempFolderPath, maxPutBlobSizeLimitInMB, MB); const inputStream = fs.createReadStream(tempFile); @@ -77,7 +82,7 @@ describe("Highlevel", () => { abortSignal: AbortController.timeout(20 * 1000) // takes too long to upload the file }); } catch (err) { - assert.equal(err.name, 'AbortError'); + assert.equal(err.name, "AbortError"); } }).timeout(timeoutForLargeFileUploadingTest); @@ -99,7 +104,7 @@ describe("Highlevel", () => { assert.ok(downloadedData.equals(uploadedData)); }).timeout(timeoutForLargeFileUploadingTest); - it.only("uploadFile should work with tags", async () => { + it("uploadFile should work with tags", async () => { recorder.skip("node", "Temp file - recorder doesn't support saving the file"); const tags = { @@ -201,7 +206,7 @@ describe("Highlevel", () => { aborter.abort(); } }); - } catch (err) { } + } catch (err) {} assert.ok(eventTriggered); }); @@ -224,20 +229,24 @@ describe("Highlevel", () => { aborter.abort(); } }); - } catch (err) { } + } catch (err) {} assert.ok(eventTriggered); }); it("uploadFile should succeed with blockSize = BLOCK_BLOB_MAX_STAGE_BLOCK_BYTES", async () => { recorder.skip("node", "Temp file - recorder doesn't support saving the file"); - const tempFile = await createRandomLocalFile(tempFolderPath, BLOCK_BLOB_MAX_STAGE_BLOCK_BYTES / (1024 * 1024) + 1, 1024 * 1024); + const tempFile = await createRandomLocalFile( + tempFolderPath, + BLOCK_BLOB_MAX_STAGE_BLOCK_BYTES / (1024 * 1024) + 1, + 1024 * 1024 + ); try { await blockBlobClient.uploadFile(tempFile, { blockSize: BLOCK_BLOB_MAX_STAGE_BLOCK_BYTES, abortSignal: AbortController.timeout(20 * 1000) // takes too long to upload the file }); } catch (err) { - assert.equal(err.name, 'AbortError'); + assert.equal(err.name, "AbortError"); } fs.unlinkSync(tempFile); @@ -280,7 +289,7 @@ describe("Highlevel", () => { fs.unlinkSync(downloadFilePath); }); - it.only("uploadStream should work with tags", async () => { + it("uploadStream should work with tags", async () => { recorder.skip("node", "Temp file - recorder doesn't support saving the file"); const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); @@ -442,7 +451,7 @@ describe("Highlevel", () => { aborter.abort(); } }); - } catch (err) { } + } catch (err) {} assert.ok(eventTriggered); }); diff --git a/sdk/storage/storage-blob/test/node/sas.spec.ts b/sdk/storage/storage-blob/test/node/sas.spec.ts index b0e6eb4022dd..d8c435f26cf2 100644 --- a/sdk/storage/storage-blob/test/node/sas.spec.ts +++ b/sdk/storage/storage-blob/test/node/sas.spec.ts @@ -324,7 +324,7 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { await containerClient.delete(); }); - it.only("generateBlobSASQueryParameters should work for blob tags", async () => { + it("generateBlobSASQueryParameters should work for blob tags", async () => { const now = recorder.newDate("now"); now.setMinutes(now.getMinutes() - 5); // Skip clock skew with server @@ -862,8 +862,10 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { await containerClient.delete(); }); - it("generateAccountSASQueryParameters should work for blob version delete", async function () { - if (isBlobVersioningDisabled()) { this.skip(); } + it("generateAccountSASQueryParameters should work for blob version delete", async function() { + if (isBlobVersioningDisabled()) { + this.skip(); + } // create versions const containerName = recorder.getUniqueName("container"); @@ -906,8 +908,10 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { await containerClientwithSAS.delete(); }); - it("generateBlobSASQueryParameters should work for blob version delete", async function () { - if (isBlobVersioningDisabled()) { this.skip(); } + it("generateBlobSASQueryParameters should work for blob version delete", async function() { + if (isBlobVersioningDisabled()) { + this.skip(); + } // create versions const containerName = recorder.getUniqueName("container"); @@ -939,7 +943,7 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { ipRange: { start: "0.0.0.0", end: "255.255.255.255" }, permissions: BlobSASPermissions.parse("racwdx"), protocol: SASProtocol.HttpsAndHttp, - versionId: uploadRes.versionId, + versionId: uploadRes.versionId }, sharedKeyCredential as StorageSharedKeyCredential ); @@ -953,15 +957,17 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { }); // TODO: prepare ACCOUNT_TOKEN for the test account - it.skip("GenerateUserDelegationSAS should work for blob version delete", async function () { - if (isBlobVersioningDisabled()) { this.skip(); } + it.skip("GenerateUserDelegationSAS should work for blob version delete", async function() { + if (isBlobVersioningDisabled()) { + this.skip(); + } // Try to get blobServiceClient object with TokenCredential // when ACCOUNT_TOKEN environment variable is set let blobServiceClientWithToken: BlobServiceClient | undefined; try { blobServiceClientWithToken = getTokenBSU(); - } catch { } + } catch {} // Requires bearer token for this case which cannot be generated in the runtime // Make sure this case passed in sanity test @@ -1001,7 +1007,7 @@ describe("Shared Access Signature (SAS) generation Node.js only", () => { ipRange: { start: "0.0.0.0", end: "255.255.255.255" }, permissions: BlobSASPermissions.parse("racwdx"), protocol: SASProtocol.HttpsAndHttp, - versionId: uploadRes.versionId, + versionId: uploadRes.versionId }, userDelegationKey, accountName