Lambda Utility Methods

Here, you'll get familiar with Lambda utility methods and learn how to protect the application from abuse.

You have already used json-response.js to format JSON objects into something that Lambda can understand, but this method loaded the CORS origin from the process environment, which should be the responsibility of the Lambda entry point. You can remove the process variable and add it as another regular argument, which makes this method easy to test.

Press + to interact
module.exports = function jsonResponse(body, corsOrigin) {
return {
statusCode: 200,
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': corsOrigin
}
};
};

When only the Lambda entry point depends on the actual Lambda infrastructure, you can cover utility methods with focused in-memory tests. The next listing shows what a test for the jsonResponse function would look like using jest (again, for simplicity, only the basic test cases are included, but in a real project you would include many more boundary scenarios).

Press + to interact
const jsonResponse = require('../json-response');
describe('jsonResponse', () => {
it('responds with HTTP code 200', () => {
const result = jsonResponse('body', 'origin');
expect(result.statusCode).toEqual(200);
});
it('includes the CORS origin', () => {
const result = jsonResponse('body', 'https://gojko.net');
expect(result.headers['Access-Control-Allow-Origin'])
.toEqual('https://gojko.net');
});
it('formats objects as JSON strings', () => {
const result = jsonResponse({a: 11, b: {c: 1}});
expect(result.body).toEqual('{"a":11,"b":{"c":1}}');
});
it('uses the JSON content type', () => {
const result = jsonResponse('body', 'origin');
expect(result.headers['Content-Type'])
.toEqual('application/json');
});
});

You’ll need another utility method to report errors back. JavaScript is specific regarding error handling, because it can pass around exceptions or strings or just plain objects as errors, so you need to ensure that all the options are converted into a string. You’ll also pass a generic HTTP error code 500 back, instead of the 200 which signals success. A file called error-response.js is created in the user-form Lambda directory, with the following code.

Press + to interact
module.exports = function errorResponse(body, corsOrigin) {
return {
statusCode: 500,
body: String(body),
headers: {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': corsOrigin
}
};
};

The test cases for errorResponse would look similar to the tests for jsonResponse, so they will be omitted from this course. You can write them for homework.

Lambda entry code #

The main Lambda file now just needs to connect all the dots. It should load the configuration from environment variables, parse events and variables correctly, and format the results and errors. The show-form.js file is changed to match the following listing.

Press + to interact
const jsonResponse = require('./json-response');
const errorResponse = require('./error-response');
const RequestProcessor = require('./request-processor');
const S3PolicySigner = require('./s3-policy-signer');
exports.lambdaHandler = async (event, context) => {
try {
const uploadSigner = new S3PolicySigner(process.env.UPLOAD_S3_BUCKET);
const downloadSigner = new S3PolicySigner(process.env.THUMBNAILS_S3_BUCKET);
const requestProcessor = new RequestProcessor(
uploadSigner,
downloadSigner,
parseInt(process.env.UPLOAD_LIMIT_IN_MB),
process.env.ALLOWED_IMAGE_EXTENSIONS.split(',')
);
const result = requestProcessor.processRequest(
context.awsRequestId,
event.pathParameters.extension,
);
return jsonResponse(result, process.env.CORS_ORIGIN);
} catch (e) {
return errorResponse(e, process.env.CORS_ORIGIN);
}
};

At MindMup, we usually do not write automated tests for Lambda entry points. ...