Skip to content

Commit e9767fd

Browse files
jeskewsrchase
authored andcommitted
Simplify middleware stack and types
1 parent 10a70eb commit e9767fd

File tree

3 files changed

+362
-236
lines changed

3 files changed

+362
-236
lines changed
Lines changed: 166 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,182 @@
11
import {MiddlewareStack} from "./";
2-
import {HandlerArguments, Middleware} from "@aws/types";
2+
import {
3+
Handler,
4+
HandlerArguments,
5+
} from "@aws/types";
6+
7+
type input = Array<string>;
8+
type output = object;
9+
type handler = Handler<input, output>;
10+
11+
class ConcatMiddleware implements handler {
12+
constructor(
13+
private readonly message: string,
14+
private readonly next: handler
15+
) {}
16+
17+
handle(args: HandlerArguments<input>): Promise<output> {
18+
return this.next.handle({
19+
...args,
20+
input: args.input.concat(this.message),
21+
})
22+
}
23+
}
24+
25+
function shuffle<T>(arr: Array<T>): Array<T> {
26+
arr = [...arr];
27+
for (let i = arr.length; i > 0; i--) {
28+
const rand = Math.floor(Math.random() * i);
29+
const curr = i - 1;
30+
[arr[curr], arr[rand]] = [arr[rand], arr[curr]];
31+
}
32+
return arr;
33+
}
334

435
describe('MiddlewareStack', () => {
536
it('should resolve the stack into a composed handler', async () => {
6-
const stack = new MiddlewareStack<Array<string>, object>();
7-
stack.prependInit(next => args => next({
8-
...args,
9-
input: args.input.concat('first')
10-
}));
11-
stack.appendBuild(next => args => next({
12-
...args,
13-
input: args.input.concat('fourth')
14-
}));
15-
stack.appendInit(next => args => next({
16-
...args,
17-
input: args.input.concat('second')
18-
}));
19-
stack.prependBuild(next => args => next({
20-
...args,
21-
input: args.input.concat('third')
22-
}));
23-
stack.appendSign(next => args => next({
24-
...args,
25-
input: args.input.concat('sixth')
26-
}));
27-
stack.prependSign(next => args => next({
28-
...args,
29-
input: args.input.concat('fifth')
30-
}));
31-
32-
const inner = jest.fn(({input}: HandlerArguments<Array<string>>) => {
33-
expect(input).toEqual([
34-
'first',
35-
'second',
36-
'third',
37-
'fourth',
38-
'fifth',
39-
'sixth',
40-
]);
41-
return {};
42-
});
43-
const composed = stack.resolve(inner);
44-
await composed({input: []});
45-
46-
expect(inner.mock.calls.length).toBe(1);
37+
const stack = new MiddlewareStack<handler>();
38+
39+
const middleware = shuffle([
40+
[ConcatMiddleware.bind(null, 'second')],
41+
[ConcatMiddleware.bind(null, 'first'), {priority: 10}],
42+
[ConcatMiddleware.bind(null, 'fourth'), {step: 'build'}],
43+
[
44+
ConcatMiddleware.bind(null, 'third'),
45+
{step: 'build', priority: 1}
46+
],
47+
[ConcatMiddleware.bind(null, 'fifth'), {step: 'finalize'}],
48+
[
49+
ConcatMiddleware.bind(null, 'sixth'),
50+
{step: 'finalize', priority: -1}
51+
],
52+
]);
53+
54+
for (const [mw, options] of middleware) {
55+
stack.add(mw, options);
56+
}
57+
58+
const inner = {
59+
handle: jest.fn(({input}: HandlerArguments<input>) => {
60+
expect(input).toEqual([
61+
'first',
62+
'second',
63+
'third',
64+
'fourth',
65+
'fifth',
66+
'sixth',
67+
]);
68+
return {};
69+
})
70+
};
71+
const composed = stack.resolve(inner, {} as any);
72+
await composed.handle({input: []});
73+
74+
expect(inner.handle.mock.calls.length).toBe(1);
4775
});
4876

4977
it('should allow cloning', async () => {
50-
const stack = new MiddlewareStack<Array<string>, object>();
51-
stack.appendInit(next => args => next({
52-
...args,
53-
input: args.input.concat('second')
54-
}));
55-
stack.prependInit(next => args => next({
56-
...args,
57-
input: args.input.concat('first')
58-
}));
78+
const stack = new MiddlewareStack<handler>();
79+
stack.add(ConcatMiddleware.bind(null, 'second'));
80+
stack.add(ConcatMiddleware.bind(null, 'first'), {priority: 100});
5981

6082
const secondStack = stack.clone();
6183

62-
let inner = jest.fn(({input}: HandlerArguments<Array<string>>) => {
63-
expect(input).toEqual([
64-
'first',
65-
'second',
66-
]);
67-
return Promise.resolve({});
68-
});
69-
await secondStack.resolve(inner)({input: []});
70-
expect(inner.mock.calls.length).toBe(1);
84+
let inner = {
85+
handle: jest.fn(({input}: HandlerArguments<input>) => {
86+
expect(input).toEqual([
87+
'first',
88+
'second',
89+
]);
90+
return Promise.resolve({});
91+
})
92+
};
93+
await secondStack.resolve(inner, {} as any).handle({input: []});
94+
expect(inner.handle.mock.calls.length).toBe(1);
7195
});
7296

73-
it('should allow the removal of middleware by object identity', async () => {
74-
const stack = new MiddlewareStack<Array<string>, object>();
75-
let middleware: Middleware<Array<string>, object> = next => args => next({
76-
...args,
77-
input: args.input.concat('first')
78-
});
79-
stack.prependInit(middleware);
80-
81-
await stack.resolve(({input}: HandlerArguments<Array<string>>) => {
82-
expect(input).toEqual([
83-
'first',
84-
]);
85-
return Promise.resolve({});
86-
})({input: []});
87-
88-
stack.remove(middleware);
89-
90-
await stack.resolve(({input}: HandlerArguments<Array<string>>) => {
91-
expect(input).toEqual([]);
92-
return Promise.resolve({});
93-
})({input: []});
97+
it('should allow combining stacks', async () => {
98+
const stack = new MiddlewareStack<handler>();
99+
stack.add(ConcatMiddleware.bind(null, 'second'));
100+
stack.add(ConcatMiddleware.bind(null, 'first'), {priority: 100});
101+
102+
const secondStack = new MiddlewareStack<handler>();
103+
secondStack.add(ConcatMiddleware.bind(null, 'fourth'), {step: 'build'});
104+
secondStack.add(
105+
ConcatMiddleware.bind(null, 'third'),
106+
{step: 'build', priority: 100}
107+
);
108+
109+
let inner = {
110+
handle: jest.fn(({input}: HandlerArguments<input>) => {
111+
expect(input).toEqual([
112+
'first',
113+
'second',
114+
'third',
115+
'fourth',
116+
]);
117+
return Promise.resolve({});
118+
})
119+
};
120+
await stack.concat(secondStack).resolve(inner, {} as any)
121+
.handle({input: []});
122+
123+
expect(inner.handle.mock.calls.length).toBe(1);
94124
});
95125

96-
it('should allow the removal of middleware by name', async () => {
97-
const stack = new MiddlewareStack<Array<string>, object>();
98-
stack.prependInit(next => args => next({
99-
...args,
100-
input: args.input.concat('first')
101-
}), 'first');
102-
103-
await stack.resolve(({input}: HandlerArguments<Array<string>>) => {
104-
expect(input).toEqual([
105-
'first',
106-
]);
107-
return Promise.resolve({});
108-
})({input: []});
109-
110-
stack.remove('first');
111-
112-
await stack.resolve(({input}: HandlerArguments<Array<string>>) => {
113-
expect(input).toEqual([]);
114-
return Promise.resolve({});
115-
})({input: []});
126+
it('should allow the removal of middleware by constructor identity', async () => {
127+
const MyMiddleware = ConcatMiddleware.bind(null, 'remove me!');
128+
const stack = new MiddlewareStack<handler>();
129+
stack.add(MyMiddleware);
130+
stack.add(ConcatMiddleware.bind(null, "don't remove me"));
131+
132+
await stack.resolve({
133+
handle: ({input}: HandlerArguments<Array<string>>) => {
134+
expect(input.sort()).toEqual([
135+
"don't remove me",
136+
'remove me!',
137+
]);
138+
return Promise.resolve({});
139+
}
140+
}, {} as any).handle({input: []});
141+
142+
stack.remove(MyMiddleware);
143+
144+
await stack.resolve({
145+
handle: ({input}: HandlerArguments<Array<string>>) => {
146+
expect(input).toEqual(["don't remove me"]);
147+
return Promise.resolve({});
148+
}
149+
}, {} as any).handle({input: []});
150+
});
151+
152+
it('should allow the removal of middleware by tag', async () => {
153+
const stack = new MiddlewareStack<handler>();
154+
stack.add(
155+
ConcatMiddleware.bind(null, 'not removed'),
156+
{tags: new Set(['foo', 'bar'])}
157+
);
158+
stack.add(
159+
ConcatMiddleware.bind(null, 'remove me!'),
160+
{tags: new Set(['foo', 'bar', 'baz'])}
161+
);
162+
163+
await stack.resolve({
164+
handle: ({input}: HandlerArguments<Array<string>>) => {
165+
expect(input.sort()).toEqual([
166+
'not removed',
167+
'remove me!',
168+
]);
169+
return Promise.resolve({});
170+
}
171+
}, {} as any).handle({input: []});
172+
173+
stack.remove('baz');
174+
175+
await stack.resolve({
176+
handle: ({input}: HandlerArguments<Array<string>>) => {
177+
expect(input).toEqual(['not removed']);
178+
return Promise.resolve({});
179+
}
180+
}, {} as any).handle({input: []});
116181
});
117182
});

0 commit comments

Comments
 (0)