Skip to content

Commit

Permalink
Move to AWS Lambda promise syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed May 26, 2019
1 parent 6356e4f commit 62126d7
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 73 deletions.
92 changes: 38 additions & 54 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,53 +29,45 @@ describe("servie-lambda", () => {
memoryLimitInMB: "128"
} as any) as Context;

it("should support routers", done => {
it("should support routers", async () => {
const handler = createHandler(function() {
return new Response("response", {
status: 200
});
});

return handler(event, context, (err, res) => {
if (err) return done(err);
const res = await handler(event, context);

expect(res).toEqual({
statusCode: 200,
body: "response",
multiValueHeaders: {
"content-type": ["text/plain"],
"content-length": ["8"]
},
isBase64Encoded: false
});

return done();
expect(res).toEqual({
statusCode: 200,
body: "response",
multiValueHeaders: {
"content-type": ["text/plain"],
"content-length": ["8"]
},
isBase64Encoded: false
});
});

it("should fall through to 404", done => {
it("should fall through to 404", async () => {
const handler = createHandler((_req, next) => next());

return handler(event, context, (err, res) => {
if (err) return done(err);

expect(res).toEqual({
statusCode: 404,
body: "Cannot GET /test",
multiValueHeaders: {
"content-type": ["text/plain"],
"content-security-policy": ["default-src 'self'"],
"x-content-type-options": ["nosniff"],
"content-length": ["16"]
},
isBase64Encoded: false
});

return done();
const res = await handler(event, context);

expect(res).toEqual({
statusCode: 404,
body: "Cannot GET /test",
multiValueHeaders: {
"content-type": ["text/plain"],
"content-security-policy": ["default-src 'self'"],
"x-content-type-options": ["nosniff"],
"content-length": ["16"]
},
isBase64Encoded: false
});
});

it("should support multiple headers of the same key", done => {
it("should support multiple headers of the same key", async () => {
const handler = createHandler(() => {
return new Response(null, {
headers: {
Expand All @@ -84,37 +76,29 @@ describe("servie-lambda", () => {
});
});

return handler(event, context, (err, res) => {
if (err) return done(err);
const res = await handler(event, context);

expect(res).toEqual({
statusCode: 200,
body: "",
multiValueHeaders: {
"set-cookie": ["a=a", "b=b", "c=c"]
},
isBase64Encoded: false
});

return done();
expect(res).toEqual({
statusCode: 200,
body: "",
multiValueHeaders: {
"set-cookie": ["a=a", "b=b", "c=c"]
},
isBase64Encoded: false
});
});

it("should log and rewrite errors", done => {
it("should log and rewrite errors", async () => {
const logError = jest.fn();
const handler = createHandler(() => Promise.reject(new Error("boom")), {
logError
});

return handler(event, context, (err, res) => {
if (err) return done(err);
const res = await handler(event, context);

expect(res!.statusCode).toEqual(500);
expect(res!.isBase64Encoded).toEqual(false);
expect(res!.body).toContain("boom");
expect(logError).toHaveBeenCalled();

return done();
});
expect(res.statusCode).toEqual(500);
expect(res.isBase64Encoded).toEqual(false);
expect(res.body).toContain("boom");
expect(logError).toHaveBeenCalled();
});
});
44 changes: 25 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { errorhandler } from "servie-errorhandler";
import { finalhandler } from "servie-finalhandler";
import {
APIGatewayEvent as Event,
APIGatewayProxyHandler as Handler,
APIGatewayProxyResult as Result,
Context
} from "aws-lambda";

export { Handler, Event, Context, Result };
export { Event, Context, Result };

/**
* AWS Lambda promise handler.
*/
export type Handler = (event: Event, context: Context) => Promise<Result>;

/**
* Extends `Request` with AWS lambda context.
Expand Down Expand Up @@ -54,7 +58,7 @@ export interface Options {
* Create a server for handling AWS Lambda requests.
*/
export function createHandler(app: App, options: Options = {}): Handler {
return function(event, context, callback): void {
return function(event, context): Promise<Result> {
const { httpMethod: method } = event;
const url = format({
pathname: event.path,
Expand All @@ -64,7 +68,6 @@ export function createHandler(app: App, options: Options = {}): Handler {
const body = event.body
? Buffer.from(event.body, event.isBase64Encoded ? "base64" : "utf8")
: undefined;
let didRespond = false;

const req = new LambdaRequest(url, {
headers: event.multiValueHeaders,
Expand All @@ -79,9 +82,7 @@ export function createHandler(app: App, options: Options = {}): Handler {
production: options.production
});

function sendResponse(res: Response): Promise<void> {
if (didRespond) return Promise.resolve();

function sendResponse(res: Response): Promise<Result> {
req.signal.emit("responseStarted");

return res.buffer().then(
Expand All @@ -91,36 +92,41 @@ export function createHandler(app: App, options: Options = {}): Handler {
const isBase64Encoded = isBinary(res);
const body = buffer.toString(isBase64Encoded ? "base64" : "utf8");

didRespond = true;

// Emit stats at end of response.
req.signal.emit("responseBytes", buffer ? buffer.byteLength : 0);
req.signal.emit("responseEnded");

return callback(null, {
return {
statusCode,
multiValueHeaders,
body,
isBase64Encoded
});
};
},
err => sendResponse(mapError(err))
);
}

req.signal.on("abort", () =>
sendResponse(new Response(null, { status: 444 }))
);

// Marked request as finished.
req.signal.emit("requestStarted");
req.signal.emit("requestBytes", body ? body.byteLength : 0);
req.signal.emit("requestStarted");

Promise.resolve(app(req, finalhandler(req))).then(
res => sendResponse(res),
err => sendResponse(mapError(err))
);
return new Promise(resolve => {
let result: Promise<Result> | undefined;

req.signal.on("abort", () => {
result = sendResponse(new Response(null, { status: 444 }));
return resolve(result);
});

return resolve(
Promise.resolve(app(req, finalhandler(req))).then(
res => result || sendResponse(res),
err => result || sendResponse(mapError(err))
)
);
});
};
}

Expand Down

0 comments on commit 62126d7

Please sign in to comment.