Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/migration/v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This doc summarises the breaking changes introduced in v2 and what you need to d
- [Configuration](#configuration)
- [Wrapping a function](#wrapping-a-function)
- [Dependency injection](#dependency-injection)
- [Services](#services)
- [RequestService](#requestservice)
- [Models](#models)
- [StatusModel](#statusmodel)

Expand Down Expand Up @@ -150,6 +152,14 @@ The `definitions` property has been removed.

The `getEvent`, `getContext` and `getConfiguration` methods have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly.

## Services

There are some small breaking changes to the built-in services. We've tried to minimise disruption here, so most of these are unlikely to affect real-world applications, but are included in the 2.0.0 release as a precaution.

### `RequestService`

Header names returned by `getAllHeaders` are now lowercased. This is consistent with many other libraries (e.g. `http`, `axios`) and makes it easier to work with HTTP headers. In some cases it may be easier to change existing code to use `getHeader`, which has provided case-insensitive access to headers since [v1.2.0](https://github.com/comicrelief/lambda-wrapper/releases/tag/v1.2.0).

## Models

The `Model` base class has been removed. It's hard to make it type-safe (it tries to dynamically call setter methods) and we do modelling and validation differently now, using our [data-models](https://github.com/comicrelief/data-models) repo which is based around [Yup](https://github.com/jquense/yup).
Expand Down
4 changes: 2 additions & 2 deletions docs/services/RequestService.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export default lambdaWrapper.wrap(async (di) => {

### Headers

- `getAllHeaders` returns an object containing all headers
- `getHeader` returns the value of an HTTP header
- `getAllHeaders` returns an object containing all HTTP headers, with all keys lowercase
- `getHeader` returns the value of a single HTTP header
- `getAuthorizationToken` extracts a Bearer token from the `Authorization` header

### Body
Expand Down
20 changes: 12 additions & 8 deletions src/services/RequestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,19 @@ export default class RequestService extends DependencyAwareClass {
/**
* Get all HTTP headers included in the request.
*
* Header names are converted to lowercase.
*
* @returns An object with a key for each header.
*/
getAllHeaders() {
const event = this.getContainer().getEvent() as APIGatewayProxyEvent;
return { ...event.headers };
getAllHeaders(): Record<string, string | undefined> {
const event = this.di.event as APIGatewayProxyEvent;
if (!event.headers) {
return {};
}
return Object.fromEntries(
Object.entries(event.headers)
.map(([key, value]) => [key.toLowerCase(), value]),
);
}

/**
Expand All @@ -86,12 +94,8 @@ export default class RequestService extends DependencyAwareClass {
*/
getHeader(name: string, whenMissing = ''): string {
const headers = this.getAllHeaders();
if (!headers) {
return whenMissing;
}
const lowerName = name.toLowerCase();
const key = Object.keys(headers).find((k) => k.toLowerCase() === lowerName);
return (key && headers[key]) || whenMissing;
return headers[lowerName] ?? whenMissing;
}

/**
Expand Down
30 changes: 29 additions & 1 deletion tests/unit/services/RequestService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,35 @@ describe('unit.services.RequestService', () => {
const request = getRequestService(event);

it('should return all headers from the event', () => {
expect(request.getAllHeaders()).toStrictEqual(event.headers);
const result = request.getAllHeaders();
expect(result).toEqual({
/* eslint-disable quote-props */
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-encoding': 'gzip, deflate',
'accept-language': 'en-us',
'cloudfront-forwarded-proto': 'https',
'cloudfront-is-desktop-viewer': 'true',
'cloudfront-is-mobile-viewer': 'false',
'cloudfront-is-smarttv-viewer': 'false',
'cloudfront-is-tablet-viewer': 'false',
'cloudfront-viewer-country': 'US',
'cookie': '__gads=ID=d51d609e5753330d:T=1443694116:S=ALNI_MbjWKzLwdEpWZ5wR5WXRI2dtjIpHw; __qca=P0-179798513-1443694132017; _ga=GA1.2.344061584.1441769647',
'host': 'xxx.execute-api.us-east-1.amazonaws.com',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/601.6.17 (KHTML, like Gecko) Version/9.1.1 Safari/601.6.17',
'via': '1.1 c8a5bb0e20655459eaam174e5c41443b.cloudfront.net (CloudFront)',
'x-amz-cf-id': 'z7Ds7oXaY8hgUn7lcedZjoIoxyvnzF6ycVzBdQmhn3QnOPEjJz4BrQ==',
'x-forwarded-for': '221.24.103.21, 54.242.148.216',
'x-forwarded-port': '443',
'x-forwarded-proto': 'https',
/* eslint-enable quote-props */
});
});

it('should convert header names to lowercase', () => {
const result = request.getAllHeaders();
Object.keys(result).forEach((name) => {
expect(name).toEqual(name.toLowerCase());
});
});
});

Expand Down