Skip to content

Commit 3e1cfd2

Browse files
committed
Use aws-lambda types and multi value headers
1 parent dd0c181 commit 3e1cfd2

File tree

4 files changed

+66
-147
lines changed

4 files changed

+66
-147
lines changed

package-lock.json

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"servie": "^3.0.0"
8080
},
8181
"dependencies": {
82-
"bit-string-mask": "^1.0.0",
82+
"@types/aws-lambda": "^8.10.23",
8383
"servie-errorhandler": "^1.2.0",
8484
"servie-finalhandler": "^1.1.0",
8585
"servie-http": "^1.1.0"

src/index.spec.ts

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,26 @@ describe("servie-lambda", () => {
99
httpMethod: "GET",
1010
body: null,
1111
resource: "/test",
12-
headers: null,
13-
queryStringParameters: null,
12+
headers: {},
13+
multiValueHeaders: {
14+
Test: ["a", "b", "c"]
15+
},
16+
isBase64Encoded: false,
17+
queryStringParameters: {},
18+
multiValueQueryStringParameters: {},
19+
stageVariables: {},
1420
requestContext: {
1521
identity: {
16-
sourceIp: ""
22+
sourceIp: "127.0.0.1"
1723
}
18-
}
24+
} as any
1925
};
2026

21-
const context = {
27+
const context = ({
2228
functionName: "",
2329
functionVersion: "$LATEST",
2430
memoryLimitInMB: "128"
25-
} as Context;
31+
} as any) as Context;
2632

2733
it("should support routers", done => {
2834
const handler = createHandler(function() {
@@ -34,17 +40,15 @@ describe("servie-lambda", () => {
3440
);
3541
});
3642

37-
return handler(event, context, (err: Error | null, res: Result) => {
38-
if (err) {
39-
return done(err);
40-
}
43+
return handler(event, context, (err, res) => {
44+
if (err) return done(err);
4145

4246
expect(res).toEqual({
4347
statusCode: 200,
4448
body: "response",
45-
headers: {
46-
"content-type": "text/plain",
47-
"content-length": "8"
49+
multiValueHeaders: {
50+
"Content-Type": ["text/plain"],
51+
"Content-Length": ["8"]
4852
},
4953
isBase64Encoded: false
5054
});
@@ -56,19 +60,17 @@ describe("servie-lambda", () => {
5660
it("should fall through to 404", done => {
5761
const handler = createHandler((_req, next) => next());
5862

59-
return handler(event, context, (err: Error | null, res: Result) => {
60-
if (err) {
61-
return done(err);
62-
}
63+
return handler(event, context, (err, res) => {
64+
if (err) return done(err);
6365

6466
expect(res).toEqual({
6567
statusCode: 404,
6668
body: "Cannot GET /test",
67-
headers: {
68-
"content-type": "text/plain",
69-
"content-security-policy": "default-src 'self'",
70-
"x-content-type-options": "nosniff",
71-
"content-length": "16"
69+
multiValueHeaders: {
70+
"Content-Type": ["text/plain"],
71+
"Content-Security-Policy": ["default-src 'self'"],
72+
"X-Content-Type-Options": ["nosniff"],
73+
"Content-Length": ["16"]
7274
},
7375
isBase64Encoded: false
7476
});
@@ -86,18 +88,14 @@ describe("servie-lambda", () => {
8688
});
8789
});
8890

89-
return handler(event, context, (err: Error | null, res: Result) => {
90-
if (err) {
91-
return done(err);
92-
}
91+
return handler(event, context, (err, res) => {
92+
if (err) return done(err);
9393

9494
expect(res).toEqual({
9595
statusCode: 200,
9696
body: "",
97-
headers: {
98-
"set-cookie": "a=a",
99-
"Set-cookie": "b=b",
100-
"sEt-cookie": "c=c"
97+
multiValueHeaders: {
98+
"Set-Cookie": ["a=a", "b=b", "c=c"]
10199
},
102100
isBase64Encoded: false
103101
});
@@ -112,14 +110,12 @@ describe("servie-lambda", () => {
112110
logError
113111
});
114112

115-
return handler(event, context, (err: Error | null, res: Result) => {
116-
if (err) {
117-
return done(err);
118-
}
113+
return handler(event, context, (err, res) => {
114+
if (err) return done(err);
119115

120-
expect(res.statusCode).toEqual(500);
121-
expect(res.isBase64Encoded).toEqual(false);
122-
expect(res.body).toContain("boom");
116+
expect(res!.statusCode).toEqual(500);
117+
expect(res!.isBase64Encoded).toEqual(false);
118+
expect(res!.body).toContain("boom");
123119
expect(logError).toHaveBeenCalled();
124120

125121
return done();

src/index.ts

Lines changed: 21 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -4,79 +4,14 @@ import { HttpRequest, HttpRequestOptions } from "servie-http";
44
import { createBody } from "servie/dist/body/node";
55
import { errorhandler } from "servie-errorhandler";
66
import { finalhandler } from "servie-finalhandler";
7-
import { mask } from "bit-string-mask";
7+
import {
8+
APIGatewayEvent as Event,
9+
APIGatewayProxyHandler as Handler,
10+
APIGatewayProxyResult as Result,
11+
Context
12+
} from "aws-lambda";
813

9-
/**
10-
* AWS Lambda event object.
11-
*/
12-
export interface Event {
13-
path: string;
14-
httpMethod: string;
15-
body: string | null;
16-
isBase64Encoded?: boolean;
17-
resource: string;
18-
headers: {
19-
[key: string]: string;
20-
} | null;
21-
queryStringParameters: {
22-
[key: string]: string;
23-
} | null;
24-
pathParameters: {
25-
[key: string]: string;
26-
} | null;
27-
requestContext: {
28-
identity: {
29-
sourceIp: string;
30-
};
31-
};
32-
}
33-
34-
/**
35-
* AWS lambda context object.
36-
*
37-
* Reference: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
38-
*/
39-
export interface Context {
40-
getRemainingTimeInMillis: () => number;
41-
callbackWaitsForEmptyEventLoop: boolean;
42-
functionName: string;
43-
functionVersion: string;
44-
invokedFunctionArn: string;
45-
memoryLimitInMB: string;
46-
awsRequestId: string;
47-
logGroupName: string;
48-
logStreamName: string;
49-
identity: { cognitoIdentityId: string; cognitoIdentityPoolId: string } | null;
50-
clientContext: {
51-
client: {
52-
installation_id: string;
53-
app_title: string;
54-
app_version_name: string;
55-
app_version_code: string;
56-
app_package_name: string;
57-
};
58-
Custom: any;
59-
env: {
60-
platform_version: string;
61-
platform: string;
62-
make: string;
63-
model: string;
64-
locale: string;
65-
};
66-
} | null;
67-
}
68-
69-
/**
70-
* Standard lambda HTTP response.
71-
*/
72-
export interface Result {
73-
body?: string;
74-
statusCode?: number;
75-
headers?: {
76-
[key: string]: string | string[];
77-
};
78-
isBase64Encoded?: boolean;
79-
}
14+
export { Handler, Event, Context, Result };
8015

8116
/**
8217
* Extends HTTP requests with AWS lambda context.
@@ -117,19 +52,15 @@ export interface Options {
11752
/**
11853
* Create a server for handling AWS Lambda requests.
11954
*/
120-
export function createHandler(app: App, options: Options = {}) {
121-
return function(
122-
event: Event,
123-
context: Context,
124-
cb: (err: Error | null, res: Result) => void
125-
): void {
55+
export function createHandler(app: App, options: Options = {}): Handler {
56+
return function(event, context, callback): void {
12657
const { httpMethod: method } = event;
12758
const url = format({
12859
pathname: event.path,
129-
query: event.queryStringParameters
60+
query: event.multiValueQueryStringParameters
13061
});
13162
const isBinary = options.isBinary || (() => false);
132-
const headers = createHeaders(event.headers);
63+
const headers = createHeaders(event.multiValueHeaders);
13364
const rawBody = event.body
13465
? Buffer.from(event.body, event.isBase64Encoded ? "base64" : "utf8")
13566
: undefined;
@@ -165,7 +96,7 @@ export function createHandler(app: App, options: Options = {}) {
16596
.arrayBuffer()
16697
.then(buffer => {
16798
const { statusCode } = res;
168-
const headers = toHeaders(res.allHeaders);
99+
const multiValueHeaders = toMultiValueHeaders(res.allHeaders);
169100
const isBase64Encoded = isBinary(res);
170101
const body = Buffer.from(buffer).toString(
171102
isBase64Encoded ? "base64" : "utf8"
@@ -177,7 +108,12 @@ export function createHandler(app: App, options: Options = {}) {
177108
res.finished = true;
178109
res.bytesTransferred = buffer ? buffer.byteLength : 0;
179110

180-
return cb(null, { statusCode, headers, body, isBase64Encoded });
111+
return callback(null, {
112+
statusCode,
113+
multiValueHeaders,
114+
body,
115+
isBase64Encoded
116+
});
181117
})
182118
.catch(err => sendResponse(mapError(err)));
183119
}
@@ -203,21 +139,8 @@ export function createHandler(app: App, options: Options = {}) {
203139
/**
204140
* Return a lambda compatible object of headers.
205141
*/
206-
function toHeaders(headers: Headers) {
207-
const result = Object.create(null);
208-
const obj = headers.asObject();
209-
210-
for (const key of Object.keys(obj)) {
211-
const val = obj[key];
212-
213-
if (Array.isArray(val)) {
214-
for (let i = 0; i < val.length; i++) {
215-
result[mask(key, i)] = val[i];
216-
}
217-
} else {
218-
result[key] = val;
219-
}
220-
}
221-
142+
function toMultiValueHeaders(headers: Headers) {
143+
const result: NonNullable<Result["multiValueHeaders"]> = Object.create(null);
144+
for (const key of headers.keys()) result[key] = headers.getAll(key);
222145
return result;
223146
}

0 commit comments

Comments
 (0)