diff --git a/sdk/core/core-client-paging-rest/package.json b/sdk/core/core-client-paging-rest/package.json index 0ccae26e8203..3768472aacd3 100644 --- a/sdk/core/core-client-paging-rest/package.json +++ b/sdk/core/core-client-paging-rest/package.json @@ -19,7 +19,7 @@ "docs": "typedoc --excludePrivate --excludeNotExported --excludeExternals --stripInternal --mode file --out ./dist/docs ./src", "execute:samples": "echo skipped", "extract-api": "tsc -p . && api-extractor run --local", - "format": "prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"samples-dev/**/*.ts\" \"*.{js,json}\"", + "format": "prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\"", "integration-test:browser": "echo skipped", "integration-test:node": "echo skipped", "integration-test": "npm run integration-test:node && npm run integration-test:browser", @@ -58,7 +58,7 @@ "sideEffects": false, "prettier": "@azure/eslint-plugin-azure-sdk/prettier.json", "dependencies": { - "@azure/core-paging": "^1.1.1", + "@azure/core-paging": "^1.2.0", "@azure/core-rest-pipeline": "^1.1.0", "@azure-rest/core-client": "1.0.0-beta.6", "tslib": "^2.2.0" diff --git a/sdk/core/core-client-paging-rest/review/core-client-paging.api.md b/sdk/core/core-client-paging-rest/review/core-client-paging.api.md index e3a6c7305a9c..b94c4ad5e450 100644 --- a/sdk/core/core-client-paging-rest/review/core-client-paging.api.md +++ b/sdk/core/core-client-paging-rest/review/core-client-paging.api.md @@ -17,7 +17,7 @@ export interface PaginateOptions { } // @public -export function paginateResponse(client: Client, initialResponse: HttpResponse, options?: PaginateOptions): PagedAsyncIterableIterator; +export function paginateResponse(client: Client, initialResponse: HttpResponse, options?: PaginateOptions): PagedAsyncIterableIterator; ``` diff --git a/sdk/core/core-client-paging-rest/src/paginate.ts b/sdk/core/core-client-paging-rest/src/paginate.ts index 9aa5796ddabe..e9dd02175757 100644 --- a/sdk/core/core-client-paging-rest/src/paginate.ts +++ b/sdk/core/core-client-paging-rest/src/paginate.ts @@ -9,7 +9,7 @@ import { HttpResponse, PathUncheckedResponse, } from "@azure-rest/core-client"; -import { PagedAsyncIterableIterator } from "@azure/core-paging"; +import { getPagedAsyncIterator, PagedAsyncIterableIterator, PagedResult } from "@azure/core-paging"; const Http2xxStatusCodes = ["200", "201", "202", "203", "204", "205", "206", "207", "208", "226"]; @@ -42,67 +42,36 @@ export interface PaginateOptions { * @param options - Options to use custom property names for pagination * @returns - return a PagedAsyncIterableIterator that can be used to iterate the elements */ -export function paginateResponse( +export function paginateResponse( client: Client, initialResponse: HttpResponse, options: PaginateOptions = {} -): PagedAsyncIterableIterator { - const iter = listAll(client, initialResponse, options); - return { - next() { - return iter.next(); - }, - [Symbol.asyncIterator]() { - return this; - }, - byPage: () => { - return listPage(client, initialResponse, options); +): PagedAsyncIterableIterator { + let firstRun = true; + const pagedResult: PagedResult = { + firstPageLink: "", + async getPage(pageLink: string) { + const result = firstRun ? initialResponse : await client.pathUnchecked(pageLink).get(); + firstRun = false; + checkPagingRequest(result); + const nextLink = getNextLink(result.body, options); + const values = getElements(result.body, options); + return { + page: values, + // According to x-ms-pageable is the nextLinkName is set to null we should only + // return the first page and skip any additional queries even if the initial response + // contains a nextLink. + nextPageLink: options.nextLinkName === null ? undefined : nextLink, + }; }, }; -} - -async function* listAll( - client: Client, - initialResponse: PathUncheckedResponse, - paginateOptions: PaginateOptions -): AsyncIterableIterator { - for await (const page of listPage(client, initialResponse, paginateOptions)) { - yield* page; - } -} - -async function* listPage[]>( - client: Client, - initialResponse: PathUncheckedResponse, - options: PaginateOptions -): AsyncIterableIterator { - let result = initialResponse; - checkPagingRequest(result); - let nextLink = getNextLink(result.body, options); - let values = getElements(result.body, options); - - yield values; - - // According to x-ms-pageable is the nextLinkName is set to null we should only - // return the first page and skip any additional queries even if the initial response - // contains a nextLink. - if (options.nextLinkName === null) { - return; - } - - while (nextLink) { - result = await client.pathUnchecked(nextLink).get(); - checkPagingRequest(result); - nextLink = getNextLink(result.body, options); - values = getElements(result.body, options); - yield values; - } + return getPagedAsyncIterator(pagedResult); } /** * Checks if a request failed */ -function checkPagingRequest(response: PathUncheckedResponse) { +function checkPagingRequest(response: PathUncheckedResponse): void { if (!Http2xxStatusCodes.includes(response.status)) { throw createRestError( `Pagination failed with unexpected statusCode ${response.status}`, @@ -114,9 +83,9 @@ function checkPagingRequest(response: PathUncheckedResponse) { /** * Gets for the value of nextLink in the body. If a custom nextLinkName was provided, it will be used instead of default */ -function getNextLink(body: Record, paginateOptions: PaginateOptions = {}) { +function getNextLink(body: unknown, paginateOptions: PaginateOptions = {}): string | undefined { const nextLinkName = paginateOptions.nextLinkName ?? DEFAULT_NEXTLINK; - const nextLink = body[nextLinkName]; + const nextLink = (body as Record)[nextLinkName]; if (typeof nextLink !== "string" && typeof nextLink !== "undefined") { throw new Error(`Body Property ${nextLinkName} should be a string or undefined`); @@ -129,12 +98,9 @@ function getNextLink(body: Record, paginateOptions: PaginateOpt * Gets the elements of the current request in the body. By default it will look in the `value` property unless * a different value for itemName has been provided as part of the options. */ -function getElements( - body: Record, - paginateOptions: PaginateOptions = {} -): T[] { +function getElements(body: unknown, paginateOptions: PaginateOptions = {}): T[] { const valueName = paginateOptions?.itemName ?? DEFAULT_VALUES; - const value = body[valueName]; + const value = (body as Record)[valueName]; if (!Array.isArray(value)) { throw new Error(`Body Property ${valueName} is not an array`); diff --git a/sdk/core/core-client-paging-rest/test/paginate.spec.ts b/sdk/core/core-client-paging-rest/test/paginate.spec.ts index 28404e7975cc..d10b2bff2616 100644 --- a/sdk/core/core-client-paging-rest/test/paginate.spec.ts +++ b/sdk/core/core-client-paging-rest/test/paginate.spec.ts @@ -229,7 +229,7 @@ interface MockResponse { * @param response - Responses to return, the actual request url is matched to one of the paths in the responses and the defined object is returned. * if no path matches a 404 error is returned */ -function mockResponse(client: Client, responses: MockResponse[]) { +function mockResponse(client: Client, responses: MockResponse[]): void { let count = 0; client.pipeline.addPolicy({