Skip to content

Commit 88980bc

Browse files
authored
fix: handle multi-part token paths in paginator (#1160)
1 parent 1b36797 commit 88980bc

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

.changeset/four-steaks-sip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": patch
3+
---
4+
5+
handle multi-part input token in paginator
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { PaginationConfiguration } from "@smithy/types";
2+
3+
import { createPaginator } from "./createPaginator";
4+
5+
describe(createPaginator.name, () => {
6+
class Client {
7+
private pages = 5;
8+
async send(command: CommandObjectToken | CommandStringToken) {
9+
if (--this.pages > 0) {
10+
return {
11+
outToken: {
12+
outToken2: {
13+
outToken3: "TOKEN_VALUE",
14+
},
15+
},
16+
};
17+
}
18+
return {};
19+
}
20+
}
21+
class CommandObjectToken {
22+
public constructor(public input: any) {
23+
expect(input).toEqual({
24+
sizeToken: 100,
25+
inToken: {
26+
outToken2: {
27+
outToken3: "TOKEN_VALUE",
28+
},
29+
},
30+
});
31+
}
32+
}
33+
34+
class CommandStringToken {
35+
public constructor(public input: any) {
36+
expect(input).toEqual({
37+
sizeToken: 100,
38+
inToken: "TOKEN_VALUE",
39+
});
40+
}
41+
}
42+
43+
it("should create a paginator", async () => {
44+
const paginate = createPaginator<PaginationConfiguration, { inToken?: string }, { outToken: string }>(
45+
Client,
46+
CommandObjectToken,
47+
"inToken",
48+
"outToken",
49+
"sizeToken"
50+
);
51+
52+
let pages = 0;
53+
54+
for await (const page of paginate(
55+
{
56+
client: new Client() as any,
57+
pageSize: 100,
58+
startingToken: {
59+
outToken2: {
60+
outToken3: "TOKEN_VALUE",
61+
},
62+
},
63+
},
64+
{}
65+
)) {
66+
pages += 1;
67+
if (pages === 5) {
68+
expect(page.outToken).toBeUndefined();
69+
} else {
70+
expect(page.outToken).toEqual({
71+
outToken2: {
72+
outToken3: "TOKEN_VALUE",
73+
},
74+
});
75+
}
76+
}
77+
78+
expect(pages).toEqual(5);
79+
});
80+
81+
it("should handle deep paths", async () => {
82+
const paginate = createPaginator<
83+
PaginationConfiguration,
84+
{ inToken?: string },
85+
{
86+
outToken: {
87+
outToken2: {
88+
outToken3: string;
89+
};
90+
};
91+
}
92+
>(Client, CommandStringToken, "inToken", "outToken.outToken2.outToken3", "sizeToken");
93+
94+
let pages = 0;
95+
96+
for await (const page of paginate(
97+
{
98+
client: new Client() as any,
99+
pageSize: 100,
100+
startingToken: "TOKEN_VALUE",
101+
},
102+
{}
103+
)) {
104+
pages += 1;
105+
if (pages === 5) {
106+
expect(page.outToken).toBeUndefined();
107+
} else {
108+
expect(page.outToken.outToken2.outToken3).toEqual("TOKEN_VALUE");
109+
}
110+
}
111+
112+
expect(pages).toEqual(5);
113+
});
114+
});

packages/core/src/pagination/createPaginator.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,25 @@ export function createPaginator<
4949
}
5050
yield page;
5151
const prevToken = token;
52-
token = (page as any)[outputTokenName];
52+
token = get(page, outputTokenName);
5353
hasNext = !!(token && (!config.stopOnSameToken || token !== prevToken));
5454
}
5555
// @ts-ignore
5656
return undefined;
5757
};
5858
}
59+
60+
/**
61+
* @internal
62+
*/
63+
const get = (fromObject: any, path: string): any => {
64+
let cursor = fromObject;
65+
const pathComponents = path.split(".");
66+
for (const step of pathComponents) {
67+
if (!cursor || typeof cursor !== "object") {
68+
return undefined;
69+
}
70+
cursor = cursor[step];
71+
}
72+
return cursor;
73+
};

0 commit comments

Comments
 (0)