Skip to content

Commit 2aff9df

Browse files
icholykuhe
andauthored
feat(pagination): add middleware support (#1480)
* feat(pagination): add middleware support * use command callback * formatting * update changeset --------- Co-authored-by: George Fu <[email protected]>
1 parent 292c134 commit 2aff9df

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

.changeset/fast-cups-speak.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@smithy/types": minor
3+
"@smithy/core": minor
4+
---
5+
6+
Added middleware support to pagination

packages/core/src/pagination/createPaginator.spec.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PaginationConfiguration } from "@smithy/types";
2-
import { describe, expect, test as it } from "vitest";
2+
import { afterEach, describe, expect, test as it, vi } from "vitest";
33

44
import { createPaginator } from "./createPaginator";
55

@@ -20,6 +20,10 @@ describe(createPaginator.name, () => {
2020
}
2121
}
2222
class CommandObjectToken {
23+
public middlewareStack = {
24+
add: vi.fn(),
25+
addRelativeTo: vi.fn(),
26+
};
2327
public constructor(public input: any) {
2428
expect(input).toEqual({
2529
sizeToken: 100,
@@ -33,6 +37,10 @@ describe(createPaginator.name, () => {
3337
}
3438

3539
class CommandStringToken {
40+
public middlewareStack = {
41+
add: vi.fn(),
42+
addRelativeTo: vi.fn(),
43+
};
3644
public constructor(public input: any) {
3745
expect(input).toEqual({
3846
sizeToken: 100,
@@ -41,6 +49,10 @@ describe(createPaginator.name, () => {
4149
}
4250
}
4351

52+
afterEach(() => {
53+
vi.resetAllMocks();
54+
});
55+
4456
it("should create a paginator", async () => {
4557
const paginate = createPaginator<PaginationConfiguration, { inToken?: string }, { outToken: string }>(
4658
Client,
@@ -112,4 +124,56 @@ describe(createPaginator.name, () => {
112124

113125
expect(pages).toEqual(5);
114126
});
127+
128+
it("should allow modification of the instantiated command", async () => {
129+
const paginate = createPaginator<PaginationConfiguration, { inToken?: string }, { outToken: string }>(
130+
Client,
131+
CommandObjectToken,
132+
"inToken",
133+
"outToken",
134+
"sizeToken"
135+
);
136+
137+
let pages = 0;
138+
const client: any = new Client();
139+
vi.spyOn(client, "send");
140+
const config = {
141+
client,
142+
pageSize: 100,
143+
startingToken: {
144+
outToken2: {
145+
outToken3: "TOKEN_VALUE",
146+
},
147+
},
148+
withCommand(command) {
149+
command.middlewareStack.add((next) => (args) => next(args));
150+
command.middlewareStack.addRelativeTo((next: any) => (args: any) => next(args), {
151+
toMiddleware: "",
152+
relation: "before",
153+
});
154+
expect(command.middlewareStack.add).toHaveBeenCalledTimes(1);
155+
expect(command.middlewareStack.addRelativeTo).toHaveBeenCalledTimes(1);
156+
return command;
157+
},
158+
} as Parameters<typeof paginate>[0];
159+
vi.spyOn(config, "withCommand");
160+
161+
for await (const page of paginate(config, {})) {
162+
pages += 1;
163+
if (pages === 5) {
164+
expect(page.outToken).toBeUndefined();
165+
} else {
166+
expect(page.outToken).toEqual({
167+
outToken2: {
168+
outToken3: "TOKEN_VALUE",
169+
},
170+
});
171+
}
172+
}
173+
174+
expect(pages).toEqual(5);
175+
expect(client.send).toHaveBeenCalledTimes(5);
176+
expect(config.withCommand).toHaveBeenCalledTimes(5);
177+
expect(config.withCommand).toHaveBeenCalledWith(expect.any(CommandObjectToken));
178+
});
115179
});

packages/core/src/pagination/createPaginator.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Client, PaginationConfiguration, Paginator } from "@smithy/types";
1+
import type { Client, Command, PaginationConfiguration, Paginator } from "@smithy/types";
22

33
/**
44
* @internal
@@ -7,9 +7,12 @@ const makePagedClientRequest = async <ClientType extends Client<any, any, any>,
77
CommandCtor: any,
88
client: ClientType,
99
input: InputType,
10+
withCommand: (command: Command<any, any, any, any, any>) => typeof command = (_) => _,
1011
...args: any[]
1112
): Promise<OutputType> => {
12-
return await client.send(new CommandCtor(input), ...args);
13+
let command = new CommandCtor(input);
14+
command = withCommand(command);
15+
return await client.send(command, ...args);
1316
};
1417

1518
/**
@@ -43,7 +46,13 @@ export function createPaginator<
4346
(input as any)[pageSizeTokenName] = (input as any)[pageSizeTokenName] ?? config.pageSize;
4447
}
4548
if (config.client instanceof ClientCtor) {
46-
page = await makePagedClientRequest(CommandCtor, config.client, input, ...additionalArguments);
49+
page = await makePagedClientRequest(
50+
CommandCtor,
51+
config.client,
52+
input,
53+
config.withCommand,
54+
...additionalArguments
55+
);
4756
} else {
4857
throw new Error(`Invalid client, expected instance of ${ClientCtor.name}`);
4958
}

packages/types/src/pagination.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Client } from "./client";
1+
import type { Client } from "./client";
2+
import type { Command } from "./command";
23

34
/**
45
* @public
@@ -25,4 +26,10 @@ export interface PaginationConfiguration {
2526
* instead of when it is not present.
2627
*/
2728
stopOnSameToken?: boolean;
29+
/**
30+
* @param command - reference to the instantiated command. This callback is executed
31+
* prior to sending the command with the paginator's client.
32+
* @returns the original command or a replacement.
33+
*/
34+
withCommand?: (command: Command<any, any, any, any, any>) => typeof command;
2835
}

0 commit comments

Comments
 (0)