-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
699 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{ | ||
"version": "2.0", | ||
"routeKey": "$default", | ||
"rawPath": "/my/path", | ||
"rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", | ||
"cookies": ["cookie1", "cookie2"], | ||
"headers": { | ||
"header1": "value1", | ||
"header2": "value1,value2" | ||
}, | ||
"queryStringParameters": { | ||
"parameter1": "value1,value2", | ||
"parameter2": "value" | ||
}, | ||
"requestContext": { | ||
"accountId": "123456789012", | ||
"apiId": "api-id", | ||
"authentication": { | ||
"clientCert": { | ||
"clientCertPem": "CERT_CONTENT", | ||
"subjectDN": "www.example.com", | ||
"issuerDN": "Example issuer", | ||
"serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", | ||
"validity": { | ||
"notBefore": "May 28 12:30:02 2019 GMT", | ||
"notAfter": "Aug 5 09:36:04 2021 GMT" | ||
} | ||
} | ||
}, | ||
"authorizer": { | ||
"jwt": { | ||
"claims": { | ||
"claim1": "value1", | ||
"claim2": "value2" | ||
}, | ||
"scopes": ["scope1", "scope2"] | ||
} | ||
}, | ||
"domainName": "id.execute-api.us-east-1.amazonaws.com", | ||
"domainPrefix": "id", | ||
"http": { | ||
"method": "POST", | ||
"path": "/my/path", | ||
"protocol": "HTTP/1.1", | ||
"sourceIp": "IP", | ||
"userAgent": "agent" | ||
}, | ||
"requestId": "id", | ||
"routeKey": "$default", | ||
"stage": "$default", | ||
"time": "12/Mar/2020:19:03:58 +0000", | ||
"timeEpoch": 1583348638390 | ||
}, | ||
"body": "Hello from Lambda", | ||
"pathParameters": { | ||
"parameter1": "value1" | ||
}, | ||
"isBase64Encoded": false, | ||
"stageVariables": { | ||
"stageVariable1": "value1", | ||
"stageVariable2": "value2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { createHttpStrategy } from '../strategies/index.js' | ||
import { | ||
apiGatewayProxyJsonParser, | ||
apiGatewayProxyParser | ||
} from '../parsers/index.js' | ||
import { | ||
apiGatewayProxyJsonSerializer, | ||
apiGatewayProxySerializer | ||
} from '../serializers/index.js' | ||
|
||
import { createHandler } from './factory.js' | ||
|
||
export const httpHandler = (options = {}) => | ||
createHandler({ | ||
parser: apiGatewayProxyParser, | ||
serializer: apiGatewayProxySerializer, | ||
createStrategy: createHttpStrategy, | ||
...options | ||
}) | ||
|
||
export const httpJsonHandler = (options = {}) => | ||
httpHandler({ | ||
parser: apiGatewayProxyJsonParser, | ||
serializer: apiGatewayProxyJsonSerializer, | ||
...options | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './http.js' | ||
export * from './invoke.js' | ||
export * from './sqs.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* API Gateway Proxy event parser. | ||
* @function apiGatewayProxyParser | ||
* @param {Object} event The event. | ||
* @returns {Object} The parsed event. | ||
* Includes searchParams as an instance of URLSearchParams. | ||
*/ | ||
|
||
/** | ||
* API Gateway Proxy JSON event parser. | ||
* @function apiGatewayProxyJsonParser | ||
* @param {Object} event The event. | ||
* @returns {Object} The parsed event with the body parsed as JSON. | ||
* Includes searchParams as an instance of URLSearchParams. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { isNil } from '@meltwater/phi' | ||
|
||
export const apiGatewayProxyParser = (event) => { | ||
const { version } = event | ||
|
||
if (version !== '2.0') { | ||
throw new Error( | ||
`Only API Gateway Proxy payload version 2.0 supported, got version ${version}` | ||
) | ||
} | ||
|
||
return { | ||
...event, | ||
searchParams: new URLSearchParams(event.rawQueryString ?? '') | ||
} | ||
} | ||
|
||
export const apiGatewayProxyJsonParser = (event) => ({ | ||
...apiGatewayProxyParser(event), | ||
body: isNil(event.body) ? undefined : JSON.parse(event.body) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import test from 'ava' | ||
|
||
import { | ||
apiGatewayProxyJsonParser, | ||
apiGatewayProxyParser | ||
} from './api-gateway-proxy.js' | ||
|
||
test('apiGatewayProxyParser: parses event', (t) => { | ||
const version = '2.0' | ||
const event = { version } | ||
t.deepEqual(apiGatewayProxyParser(event), { | ||
version, | ||
searchParams: new URLSearchParams('') | ||
}) | ||
}) | ||
|
||
test('apiGatewayProxyParser: only parses event version 2.0', (t) => { | ||
const version = '1.0' | ||
const event = { version } | ||
t.throws(() => apiGatewayProxyParser(event), { message: /version 1.0/ }) | ||
}) | ||
|
||
test('apiGatewayProxyParser: parses event with search params', (t) => { | ||
const version = '2.0' | ||
const rawQueryString = 'foo=bar' | ||
const event = { version, rawQueryString } | ||
const data = apiGatewayProxyParser(event) | ||
t.is(data.searchParams.get('foo'), 'bar') | ||
}) | ||
|
||
test('apiGatewayProxyJsonParser: parses event', (t) => { | ||
const version = '2.0' | ||
const event = { version } | ||
t.deepEqual(apiGatewayProxyJsonParser(event), { | ||
version, | ||
body: undefined, | ||
searchParams: new URLSearchParams('') | ||
}) | ||
}) | ||
|
||
test('apiGatewayProxyJsonParser: only parses event version 2.0', (t) => { | ||
const version = '1.0' | ||
const event = { version } | ||
t.throws(() => apiGatewayProxyJsonParser(event), { | ||
message: /version 1.0/ | ||
}) | ||
}) | ||
|
||
test('apiGatewayProxyJsonParser: parses event with search params', (t) => { | ||
const version = '2.0' | ||
const rawQueryString = 'foo=bar' | ||
const event = { version, rawQueryString } | ||
const data = apiGatewayProxyJsonParser(event) | ||
t.is(data.searchParams.get('foo'), 'bar') | ||
}) | ||
|
||
test('apiGatewayProxyJsonParser: parses event body as json', (t) => { | ||
const event = { version: '2.0', body: '{"foo":2}' } | ||
const data = apiGatewayProxyJsonParser(event) | ||
t.deepEqual(data.body, { foo: 2 }) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './api-gateway-proxy.js' | ||
export * from './identity.js' | ||
export * from './records.js' | ||
export * from './sqs.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* API Gateway Proxy event serializer. | ||
* @function apiGatewayProxySerializer | ||
* @param {Object} event The data. | ||
* @returns {Object} The serialized data. | ||
*/ | ||
|
||
/** | ||
* API Gateway Proxy JSON event serializer. | ||
* @function apiGatewayProxyJsonSerializer | ||
* @param {Object} event The data. | ||
* @returns {Object} The serialized data with the body serialized to JSON. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { isNil } from '@meltwater/phi' | ||
|
||
export const apiGatewayProxySerializer = (data) => data | ||
|
||
export const apiGatewayProxyJsonSerializer = (data) => { | ||
if (isNil(data?.body)) return data | ||
return { | ||
isBase64Encoded: false, | ||
cookies: [], | ||
statusCode: 200, | ||
...data, | ||
body: JSON.stringify(data.body), | ||
headers: { | ||
'content-type': 'application/json', | ||
...data?.headers | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import test from 'ava' | ||
|
||
import { | ||
apiGatewayProxyJsonSerializer, | ||
apiGatewayProxySerializer | ||
} from './api-gateway-proxy.js' | ||
|
||
test('apiGatewayProxySerializer: serializes', (t) => { | ||
const data = { foo: 'bar', statusCode: 201 } | ||
t.deepEqual(apiGatewayProxySerializer(data), data) | ||
}) | ||
|
||
test('apiGatewayProxyJsonSerializer: serializes data', (t) => { | ||
const data = { foo: 'bar', statusCode: 201 } | ||
t.deepEqual(apiGatewayProxyJsonSerializer(data), data) | ||
}) | ||
|
||
test('apiGatewayProxyJsonSerializer: serializes body as json', (t) => { | ||
const data = { foo: 'bar', headers: { x: 'y' }, body: { a: 2 } } | ||
t.deepEqual(apiGatewayProxyJsonSerializer(data), { | ||
...data, | ||
isBase64Encoded: false, | ||
cookies: [], | ||
statusCode: 200, | ||
body: '{"a":2}', | ||
headers: { | ||
x: 'y', | ||
'content-type': 'application/json' | ||
} | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './api-gateway-proxy.js' | ||
export * from './identity.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* Create an HTTP strategy. | ||
* Resolves and calls the processor for the event. | ||
* Swallows all errors, wraps them as a Boom object, | ||
* resolves and calls onError on the wrapped error, | ||
* and returns a matching status code response. | ||
* @function createEventStrategy | ||
* @param {Object} container The Awilix container. | ||
* @returns {Object[]} The strategy. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { noop } from '@meltwater/phi' | ||
import { boomify, isBoom } from '@hapi/boom' | ||
|
||
export const createHttpStrategy = (container) => async (event, ctx) => { | ||
const scope = container.createScope() | ||
const processor = scope.resolve('processor') | ||
const onError = scope.resolve('onError', { allowUnregistered: true }) ?? noop | ||
try { | ||
return await processor(event, ctx) | ||
} catch (err) { | ||
const error = wrapError(err) | ||
onError(error) | ||
return toErrorResponse(error) | ||
} | ||
} | ||
|
||
const wrapError = (err) => | ||
isBoom(err) ? err : boomify(err, { statusCode: 500 }) | ||
|
||
const toErrorResponse = (err) => { | ||
const { output } = err | ||
return { | ||
statusCode: output.statusCode | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import test from 'ava' | ||
import { asValue, createContainer } from 'awilix' | ||
import { unauthorized } from '@hapi/boom' | ||
|
||
import { createHttpStrategy } from './http.js' | ||
|
||
test('createHttpStrategy', async (t) => { | ||
const container = createContainer() | ||
container.register('processor', asValue(processor)) | ||
const strategy = createHttpStrategy(container) | ||
const data = await strategy({ a: 1 }, { b: 2 }) | ||
t.deepEqual(data, { | ||
ctx: { b: 2 }, | ||
event: { a: 1 } | ||
}) | ||
}) | ||
|
||
test('createHttpStrategy: calls onError', async (t) => { | ||
t.plan(2) | ||
const onError = (err) => { | ||
t.is(err.message, errMessage) | ||
} | ||
const container = createContainer() | ||
container.register('processor', asValue(processorWithError)) | ||
container.register('onError', asValue(onError)) | ||
const strategy = createHttpStrategy(container) | ||
const data = await strategy({ a: 1 }, { b: 2 }) | ||
t.deepEqual(data, { statusCode: 500 }) | ||
}) | ||
|
||
test('createHttpStrategy: handles boom error', async (t) => { | ||
const container = createContainer() | ||
container.register('processor', asValue(processorWithBoomError)) | ||
const strategy = createHttpStrategy(container) | ||
const data = await strategy({ a: 1 }, { b: 2 }) | ||
t.deepEqual(data, { statusCode: 401 }) | ||
}) | ||
|
||
const processor = async (event, ctx) => ({ event, ctx }) | ||
|
||
const errMessage = 'Mock processor error' | ||
const processorWithError = async (event, ctx) => { | ||
throw new Error(errMessage) | ||
} | ||
|
||
const processorWithBoomError = async (event, ctx) => { | ||
throw unauthorized() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './event.js' | ||
export * from './http.js' | ||
export * from './parallel.js' |
Oops, something went wrong.