-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[Storage] Async iterator for listing blobs and containers - storage-blob #3136
Changes from 19 commits
9524d20
eecb12a
28e8773
dc03069
bb41616
da1afa5
34e3772
eb3b1ca
72cd066
7d7cb2f
7922584
d1cf6d0
c8128b5
49c1da6
c272c6a
5b07b44
cdd3d2e
43d30c0
bc932b3
3fb743a
842de33
d7b7f71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
Setup: Enter your storage account name and shared key in main() | ||
*/ | ||
|
||
import { | ||
ContainerClient, | ||
BlobServiceClient, | ||
StorageClient, | ||
SharedKeyCredential, | ||
BlobClient, | ||
BlockBlobClient | ||
} from "../../src"; // Change to "@azure/storage-blob" in your package | ||
|
||
async function main() { | ||
// Enter your storage account name and shared key | ||
const account = ""; | ||
const accountKey = ""; | ||
|
||
// Use SharedKeyCredential with storage account and account key | ||
const sharedKeyCredential = new SharedKeyCredential(account, accountKey); | ||
|
||
// Use sharedKeyCredential, tokenCredential or anonymousCredential to create a pipeline | ||
const pipeline = StorageClient.newPipeline(sharedKeyCredential); | ||
|
||
// List containers | ||
const blobServiceClient = new BlobServiceClient( | ||
// When using AnonymousCredential, following url should include a valid SAS or support public access | ||
`https://${account}.blob.core.windows.net`, | ||
pipeline | ||
); | ||
|
||
// List Containers | ||
let iter1 = await blobServiceClient.listContainers(); | ||
let i = 1; | ||
for await (const container of iter1) { | ||
console.log(`Container ${i++}: ${container.name}`); | ||
} | ||
|
||
// List containers - generator syntax | ||
let iter2 = await blobServiceClient.listContainers(); | ||
i = 1; | ||
let containerItem = await iter2.next(); | ||
do { | ||
console.log(`Container ${i++}: ${containerItem.value.name}`); | ||
containerItem = await iter2.next(); | ||
} while (containerItem.value); | ||
|
||
// Create a container | ||
const containerName = `newcontainer${new Date().getTime()}`; | ||
const containerClient = ContainerClient.fromBlobServiceClient(blobServiceClient, containerName); | ||
|
||
const createContainerResponse = await containerClient.create(); | ||
console.log(`Created container ${containerName} successfully`, createContainerResponse.requestId); | ||
|
||
for (let index = 0; index < 4; index++) { | ||
// Create a blob | ||
let content = "hello"; | ||
let blobName = "newblob" + new Date().getTime(); | ||
let blobClient = BlobClient.fromContainerClient(containerClient, blobName); | ||
let blockBlobClient = BlockBlobClient.fromBlobClient(blobClient); | ||
let uploadBlobResponse = await blockBlobClient.upload(content, content.length); | ||
console.log(`Uploaded block blob ${blobName} successfully`, uploadBlobResponse.requestId); | ||
} | ||
|
||
// List blobs | ||
iter1 = await containerClient.listBlobsFlat(); | ||
i = 1; | ||
for await (const blob of iter1) { | ||
console.log(`Blob ${i++}: ${blob.name}`); | ||
} | ||
|
||
// List blobs - generator syntax | ||
iter2 = await containerClient.listBlobsFlat(); | ||
i = 1; | ||
let blobItem = await iter2.next(); | ||
do { | ||
console.log(`Blob ${i++}: ${blobItem.value.name}`); | ||
blobItem = await iter2.next(); | ||
} while (blobItem.value); | ||
} | ||
|
||
// An async method returns a Promise object, which is compatible with then().catch() coding style. | ||
main() | ||
.then(() => { | ||
console.log("Successfully executed the sample."); | ||
}) | ||
.catch((err) => { | ||
console.log(err.message); | ||
}); |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -160,9 +160,7 @@ describe("ContainerClient", () => { | |||||||||||
it("listBlobFlatSegment with default parameters", async () => { | ||||||||||||
const blobClients = []; | ||||||||||||
for (let i = 0; i < 3; i++) { | ||||||||||||
const blobClient = containerClient.createBlobClient( | ||||||||||||
getUniqueName(`blockblob/${i}`) | ||||||||||||
); | ||||||||||||
const blobClient = containerClient.createBlobClient(getUniqueName(`blockblob/${i}`)); | ||||||||||||
const blockBlobClient = blobClient.createBlockBlobClient(); | ||||||||||||
await blockBlobClient.upload("", 0); | ||||||||||||
blobClients.push(blobClient); | ||||||||||||
|
@@ -188,9 +186,7 @@ describe("ContainerClient", () => { | |||||||||||
keyb: "c" | ||||||||||||
}; | ||||||||||||
for (let i = 0; i < 2; i++) { | ||||||||||||
const blobClient = containerClient.createBlobClient( | ||||||||||||
getUniqueName(`${prefix}/${i}`) | ||||||||||||
); | ||||||||||||
const blobClient = containerClient.createBlobClient(getUniqueName(`${prefix}/${i}`)); | ||||||||||||
const blockBlobClient = blobClient.createBlockBlobClient(); | ||||||||||||
await blockBlobClient.upload("", 0, { | ||||||||||||
metadata | ||||||||||||
|
@@ -226,12 +222,44 @@ describe("ContainerClient", () => { | |||||||||||
} | ||||||||||||
}); | ||||||||||||
|
||||||||||||
it("Verify AsyncIterator for listBlobsFlat", async () => { | ||||||||||||
const blobClients = []; | ||||||||||||
const prefix = "blockblob"; | ||||||||||||
const metadata = { | ||||||||||||
keya: "a", | ||||||||||||
keyb: "c" | ||||||||||||
}; | ||||||||||||
for (let i = 0; i < 2; i++) { | ||||||||||||
const blobClient = containerClient.createBlobClient(getUniqueName(`${prefix}/${i}`)); | ||||||||||||
const blockBlobClient = blobClient.createBlockBlobClient(); | ||||||||||||
await blockBlobClient.upload("", 0, { | ||||||||||||
metadata | ||||||||||||
}); | ||||||||||||
blobClients.push(blobClient); | ||||||||||||
} | ||||||||||||
|
||||||||||||
const iterator = await containerClient.listBlobsFlat({ | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need test against other usage scenarios for async iterator? such as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we cover the cross segment scenario? Because implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we are doing it. azure-sdk-for-js/sdk/storage/storage-blob/src/ContainerClient.ts Lines 630 to 634 in bc932b3
The current implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
842de33 - Added a for-loop test case. Outputs (console.logs) to verify your question. Case 1 - ------------------------- called listBlobFlatSegment -------------------------
blockblob/0155925458974007856
blockblob/1155925459003608508
blockblob/2155925459032203396
blockblob/3155925459061100420
√ Verify AsyncIterator(for-loop syntax) for listBlobsFlat (2714ms) Case 2 - ------------------------- called listBlobFlatSegment -------------------------
blockblob/0155925451934305295
blockblob/1155925451964308353
------------------------- called listBlobFlatSegment -------------------------
blockblob/2155925451993002640
blockblob/3155925452022007968
√ Verify AsyncIterator(for-loop syntax) for listBlobsFlat (3012ms) |
||||||||||||
include: ["snapshots", "metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
prefix | ||||||||||||
}); | ||||||||||||
|
||||||||||||
let blobItem = await iterator.next(); | ||||||||||||
assert.ok(blobClients[0].url.indexOf(blobItem.value.name)); | ||||||||||||
assert.deepStrictEqual(blobItem.value.metadata, metadata); | ||||||||||||
|
||||||||||||
blobItem = await iterator.next(); | ||||||||||||
assert.ok(blobClients[1].url.indexOf(blobItem.value.name)); | ||||||||||||
assert.deepStrictEqual(blobItem.value.metadata, metadata); | ||||||||||||
|
||||||||||||
for (const blob of blobClients) { | ||||||||||||
await blob.delete(); | ||||||||||||
} | ||||||||||||
}); | ||||||||||||
|
||||||||||||
it("listBlobHierarchySegment with default parameters", async () => { | ||||||||||||
const blobClients = []; | ||||||||||||
for (let i = 0; i < 3; i++) { | ||||||||||||
const blobClient = containerClient.createBlobClient( | ||||||||||||
getUniqueName(`blockblob${i}/${i}`) | ||||||||||||
); | ||||||||||||
const blobClient = containerClient.createBlobClient(getUniqueName(`blockblob${i}/${i}`)); | ||||||||||||
const blockBlobClient = blobClient.createBlockBlobClient(); | ||||||||||||
await blockBlobClient.upload("", 0); | ||||||||||||
blobClients.push(blobClient); | ||||||||||||
|
@@ -274,45 +302,33 @@ describe("ContainerClient", () => { | |||||||||||
blobClients.push(blobClient); | ||||||||||||
} | ||||||||||||
|
||||||||||||
const result = await containerClient.listBlobHierarchySegment( | ||||||||||||
delimiter, | ||||||||||||
undefined, | ||||||||||||
{ | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 1, | ||||||||||||
prefix | ||||||||||||
} | ||||||||||||
); | ||||||||||||
const result = await containerClient.listBlobHierarchySegment(delimiter, undefined, { | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 1, | ||||||||||||
prefix | ||||||||||||
}); | ||||||||||||
assert.ok(result.serviceEndpoint.length > 0); | ||||||||||||
assert.ok(containerClient.url.indexOf(result.containerName)); | ||||||||||||
assert.deepStrictEqual(result.segment.blobPrefixes!.length, 1); | ||||||||||||
assert.deepStrictEqual(result.segment.blobItems!.length, 0); | ||||||||||||
assert.ok(blobClients[0].url.indexOf(result.segment.blobPrefixes![0].name)); | ||||||||||||
|
||||||||||||
const result2 = await containerClient.listBlobHierarchySegment( | ||||||||||||
delimiter, | ||||||||||||
result.nextMarker, | ||||||||||||
{ | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 2, | ||||||||||||
prefix | ||||||||||||
} | ||||||||||||
); | ||||||||||||
const result2 = await containerClient.listBlobHierarchySegment(delimiter, result.nextMarker, { | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 2, | ||||||||||||
prefix | ||||||||||||
}); | ||||||||||||
assert.ok(result2.serviceEndpoint.length > 0); | ||||||||||||
assert.ok(containerClient.url.indexOf(result2.containerName)); | ||||||||||||
assert.deepStrictEqual(result2.segment.blobPrefixes!.length, 1); | ||||||||||||
assert.deepStrictEqual(result2.segment.blobItems!.length, 0); | ||||||||||||
assert.ok(blobClients[0].url.indexOf(result2.segment.blobPrefixes![0].name)); | ||||||||||||
|
||||||||||||
const result3 = await containerClient.listBlobHierarchySegment( | ||||||||||||
delimiter, | ||||||||||||
undefined, | ||||||||||||
{ | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 2, | ||||||||||||
prefix: `${prefix}0${delimiter}` | ||||||||||||
} | ||||||||||||
); | ||||||||||||
const result3 = await containerClient.listBlobHierarchySegment(delimiter, undefined, { | ||||||||||||
include: ["metadata", "uncommittedblobs", "copy", "deleted"], | ||||||||||||
maxresults: 2, | ||||||||||||
prefix: `${prefix}0${delimiter}` | ||||||||||||
}); | ||||||||||||
assert.ok(result3.serviceEndpoint.length > 0); | ||||||||||||
assert.ok(containerClient.url.indexOf(result3.containerName)); | ||||||||||||
assert.deepStrictEqual(result3.nextMarker, ""); | ||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering what will happen if Aborter aborted iteration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example -
[ About 6500 containers are present in the account ]
The request was aborted
.[ X = 10000 yielded the same result ]
The request was aborted
is printed in the terminal.