Skip to content

Commit

Permalink
[@azure-rest/core-paging] Use @azure/core-paging's getPagedAsyncItera…
Browse files Browse the repository at this point in the history
…tor (#16891)

* [core-paging-rest] Use the core-paging helper in paginateResponse

* format

* lint

* update core-paging dep ver

* rerun extract api

* address feedback
  • Loading branch information
deyaaeldeen authored Aug 14, 2021
1 parent d3a52cc commit ca94e95
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 63 deletions.
4 changes: 2 additions & 2 deletions sdk/core/core-client-paging-rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface PaginateOptions {
}

// @public
export function paginateResponse<TReturn>(client: Client, initialResponse: HttpResponse, options?: PaginateOptions): PagedAsyncIterableIterator<TReturn, TReturn[]>;
export function paginateResponse<TElement>(client: Client, initialResponse: HttpResponse, options?: PaginateOptions): PagedAsyncIterableIterator<TElement>;


```
84 changes: 25 additions & 59 deletions sdk/core/core-client-paging-rest/src/paginate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down Expand Up @@ -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<TReturn>(
export function paginateResponse<TElement>(
client: Client,
initialResponse: HttpResponse,
options: PaginateOptions = {}
): PagedAsyncIterableIterator<TReturn, TReturn[]> {
const iter = listAll<TReturn>(client, initialResponse, options);
return {
next() {
return iter.next();
},
[Symbol.asyncIterator]() {
return this;
},
byPage: () => {
return listPage<TReturn>(client, initialResponse, options);
): PagedAsyncIterableIterator<TElement> {
let firstRun = true;
const pagedResult: PagedResult<TElement[]> = {
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<TElement>(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<T>(
client: Client,
initialResponse: PathUncheckedResponse,
paginateOptions: PaginateOptions
): AsyncIterableIterator<T> {
for await (const page of listPage<T>(client, initialResponse, paginateOptions)) {
yield* page;
}
}

async function* listPage<T = Record<string, unknown>[]>(
client: Client,
initialResponse: PathUncheckedResponse,
options: PaginateOptions
): AsyncIterableIterator<T[]> {
let result = initialResponse;
checkPagingRequest(result);
let nextLink = getNextLink(result.body, options);
let values = getElements<T>(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<T>(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}`,
Expand All @@ -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<string, unknown>, 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<string, unknown>)[nextLinkName];

if (typeof nextLink !== "string" && typeof nextLink !== "undefined") {
throw new Error(`Body Property ${nextLinkName} should be a string or undefined`);
Expand All @@ -129,12 +98,9 @@ function getNextLink(body: Record<string, unknown>, 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<T = unknown>(
body: Record<string, unknown>,
paginateOptions: PaginateOptions = {}
): T[] {
function getElements<T = unknown>(body: unknown, paginateOptions: PaginateOptions = {}): T[] {
const valueName = paginateOptions?.itemName ?? DEFAULT_VALUES;
const value = body[valueName];
const value = (body as Record<string, unknown>)[valueName];

if (!Array.isArray(value)) {
throw new Error(`Body Property ${valueName} is not an array`);
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-client-paging-rest/test/paginate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down

0 comments on commit ca94e95

Please sign in to comment.