From 65beb5e3ae457426b33ed7abe4c437c5adf6d080 Mon Sep 17 00:00:00 2001 From: Jeremy Meng Date: Sat, 12 Oct 2019 08:33:14 -0700 Subject: [PATCH] [Storage][File] Add paged async iterator for FileClient listHandles() Resolves #5530. --- sdk/storage/storage-file/src/FileClient.ts | 104 +++++++++++++++++- .../storage-file/test/fileclient.spec.ts | 10 +- 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/sdk/storage/storage-file/src/FileClient.ts b/sdk/storage/storage-file/src/FileClient.ts index c5c2c852b9ff..5fa0898628d2 100644 --- a/sdk/storage/storage-file/src/FileClient.ts +++ b/sdk/storage/storage-file/src/FileClient.ts @@ -28,6 +28,8 @@ import { FILE_RANGE_MAX_SIZE_BYTES, DEFAULT_HIGH_LEVEL_PARALLELISM } from "./utils/constants"; +import "@azure/core-paging"; +import { PageSettings, PagedAsyncIterableIterator } from "@azure/core-paging"; import { Credential } from "./credentials/Credential"; import { Batch } from "./utils/Batch"; import { BufferScheduler } from "./utils/BufferScheduler"; @@ -445,6 +447,17 @@ export interface FileListHandlesSegmentOptions extends CommonOptions { maxresults?: number; } +export interface FileListHandlesOptions extends CommonOptions { + /** + * An implementation of the `AbortSignalLike` interface to signal the request to cancel the operation. + * For example, use the @azure/abort-controller to create an `AbortSignal`. + * + * @type {AbortSignalLike} + * @memberof FileClearRangeOptions + */ + abortSignal?: AbortSignalLike; +} + /** * Options to configure File - File Force Close Handles Options. * @@ -1819,7 +1832,7 @@ export class FileClient extends StorageClient { * @returns {Promise} * @memberof FileURL */ - public async listHandlesSegment( + private async listHandlesSegment( marker?: string, options: FileListHandlesSegmentOptions = {} ): Promise { @@ -1850,6 +1863,95 @@ export class FileClient extends StorageClient { } } + /** + * Returns an AsyncIterableIterator for FileListHandlesResponse + * + * @private + * @param {string} [marker] A string value that identifies the portion of the list to be + * returned with the next list handles operation. The operation returns a + * marker value within the response body if the list returned was not complete. + * The marker value may then be used in a subsequent call to request the next + * set of list items. + * @param {FileListHandlesSegmentOptions} [options] Options to list handles operation. + * @returns {AsyncIterableIterator} + * @memberof FileClient + */ + private async *iterateHandleSegments( + marker?: string, + options: FileListHandlesSegmentOptions = {} + ): AsyncIterableIterator { + let listHandlesResponse; + if (!!marker || marker === undefined) { + do { + listHandlesResponse = await this.listHandlesSegment(marker, options); + marker = listHandlesResponse.nextMarker; + yield listHandlesResponse; + } while (marker); + } + } + + /** + * Returns an AsyncIterableIterator for handles + * + * @private + * @param {FileListHandlesSegmentOptions} [options] Options to list handles operation. + * @returns {AsyncIterableIterator} + * @memberof FileClient + */ + private async *listHandleItems( + options: FileListHandlesSegmentOptions = {} + ): AsyncIterableIterator { + let marker: string | undefined; + for await (const listHandlesResponse of this.iterateHandleSegments(marker, options)) { + if (listHandlesResponse.handleList) { + for (const handle of listHandlesResponse.handleList) { + yield handle; + } + } + } + } + + /** + * Returns an async iterable iterator to list all the handles. + * under the specified account. + * + * .byPage() returns an async iterable iterator to list the handles in pages. + * + * @param {FileListHandlesOptions} [options] Options to list handles operation. + * @memberof FileClient + * @returns {PagedAsyncIterableIterator} + * An asyncIterableIterator that supports paging. + */ + public listHandles( + options: FileListHandlesOptions = {} + ): PagedAsyncIterableIterator { + // an AsyncIterableIterator to iterate over handles + const iter = this.listHandleItems(options); + return { + /** + * @member {Promise} [next] The next method, part of the iteration protocol + */ + async next() { + return iter.next(); + }, + /** + * @member {Symbol} [asyncIterator] The connection to the async iterator, part of the iteration protocol + */ + [Symbol.asyncIterator]() { + return this; + }, + /** + * @member {Function} [byPage] Return an AsyncIterableIterator that works a page at a time + */ + byPage: (settings: PageSettings = {}) => { + return this.iterateHandleSegments(settings.continuationToken, { + maxresults: settings.maxPageSize, + ...options + }); + } + }; + } + /** * Force close all handles for a file. * @see https://docs.microsoft.com/en-us/rest/api/storageservices/force-close-handles diff --git a/sdk/storage/storage-file/test/fileclient.spec.ts b/sdk/storage/storage-file/test/fileclient.spec.ts index 2317a0ed5ada..a16d623a1cd2 100644 --- a/sdk/storage/storage-file/test/fileclient.spec.ts +++ b/sdk/storage/storage-file/test/fileclient.spec.ts @@ -461,7 +461,10 @@ describe("FileClient", () => { it("listHandles should work", async () => { await fileClient.create(10); - const result = await fileClient.listHandlesSegment(undefined); + const result = (await fileClient + .listHandles() + .byPage() + .next()).value; if (result.handleList !== undefined && result.handleList.length > 0) { const handle = result.handleList[0]; assert.notDeepStrictEqual(handle.handleId, undefined); @@ -493,7 +496,10 @@ describe("FileClient", () => { // TODO: Open or create a handle - const result = await fileClient.listHandlesSegment(undefined); + const result = (await fileClient + .listHandles() + .byPage() + .next()).value; if (result.handleList !== undefined && result.handleList.length > 0) { const handle = result.handleList[0]; await dirClient.forceCloseHandle(handle.handleId);