Skip to content

Commit

Permalink
fix: Fixed detection of REST API type payloads in AWS Lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Sep 4, 2024
1 parent f004ebf commit f2e756e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 17 deletions.
24 changes: 18 additions & 6 deletions lib/serverless/api-gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ function normalizeHeaders(event, lowerCaseKey = false) {

/**
* Determines if Lambda event appears to be a valid Lambda Proxy event.
* There are multiple types of events possible. They are described at
* https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html#services-apigateway-apitypes.
* Each type of event has its own event payload structure; some types have
* multiple versions of the payload structure.
*
* @param {object} event The event to inspect.
* @returns {boolean} Whether the given object contains fields necessary
Expand All @@ -106,7 +110,8 @@ function isLambdaProxyEvent(event) {
return isGatewayV1Event(event) || isGatewayV2Event(event)
}

const v1Keys = [
// See https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
const restApiV1Keys = [
'body',
'headers',
'httpMethod',
Expand All @@ -118,16 +123,23 @@ const v1Keys = [
'queryStringParameters',
'requestContext',
'resource',
'stageVariables',
'version'
'stageVariables'
].join(',')

// See https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const httpApiV1Keys = [...restApiV1Keys.split(','), 'version'].join(',')

function isGatewayV1Event(event) {
const keys = Object.keys(event).sort().join(',')
return keys === v1Keys && event?.version === '1.0'
if (keys === httpApiV1Keys && event?.version === '1.0') {
return true
}

return keys === restApiV1Keys
}

const v2Keys = [
// See https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const httpApiV2Keys = [
'body',
'cookies',
'headers',
Expand All @@ -144,7 +156,7 @@ const v2Keys = [

function isGatewayV2Event(event) {
const keys = Object.keys(event).sort().join(',')
return keys === v2Keys && event?.version === '2.0'
return keys === httpApiV2Keys && event?.version === '2.0'
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/unit/serverless/api-gateway-v2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const AwsLambda = require('../../../lib/serverless/aws-lambda')

const ATTR_DEST = require('../../../lib/config/attribute-filter').DESTINATIONS

const { gatewayV2Event: v2Event } = require('./fixtures')
const { httpApiGatewayV2Event: v2Event } = require('./fixtures')

tap.beforeEach((t) => {
// This env var suppresses console output we don't need to inspect.
Expand Down
81 changes: 76 additions & 5 deletions test/unit/serverless/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

'use strict'

// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const gatewayV1Event = {
const httpApiGatewayV1Event = {
version: '1.0',
resource: '/my/path',
path: '/my/path',
Expand Down Expand Up @@ -77,8 +76,79 @@ const gatewayV1Event = {
isBase64Encoded: false
}

// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
const restApiGatewayV1Event = {
resource: '/my/path',
path: '/my/path',
httpMethod: 'GET',
headers: {
header1: 'value1',
header2: 'value2'
},
multiValueHeaders: {
header1: ['value1'],
header2: ['value1', 'value2']
},
queryStringParameters: {
parameter1: 'value1',
parameter2: 'value'
},
multiValueQueryStringParameters: {
parameter1: ['value1', 'value2'],
parameter2: ['value']
},
requestContext: {
accountId: '123456789012',
apiId: 'id',
authorizer: {
claims: null,
scopes: null
},
domainName: 'id.execute-api.us-east-1.amazonaws.com',
domainPrefix: 'id',
extendedRequestId: 'request-id',
httpMethod: 'GET',
identity: {
accessKey: null,
accountId: null,
caller: null,
cognitoAuthenticationProvider: null,
cognitoAuthenticationType: null,
cognitoIdentityId: null,
cognitoIdentityPoolId: null,
principalOrgId: null,
sourceIp: '192.0.2.1',
user: null,
userAgent: 'user-agent',
userArn: null,
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'
}
}
},
path: '/my/path',
protocol: 'HTTP/1.1',
requestId: 'id=',
requestTime: '04/Mar/2020:19:15:17 +0000',
requestTimeEpoch: 1583349317135,
resourceId: null,
resourcePath: '/my/path',
stage: '$default'
},
pathParameters: null,
stageVariables: null,
body: 'Hello from Lambda!',
isBase64Encoded: false
}

// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
const gatewayV2Event = {
const httpApiGatewayV2Event = {
version: '2.0',
routeKey: '$default',
rawPath: '/my/path',
Expand Down Expand Up @@ -175,7 +245,8 @@ const lambaV1InvocationEvent = {
}

module.exports = {
gatewayV1Event,
gatewayV2Event,
restApiGatewayV1Event,
httpApiGatewayV1Event,
httpApiGatewayV2Event,
lambaV1InvocationEvent
}
17 changes: 12 additions & 5 deletions test/unit/serverless/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ const test = require('node:test')
const assert = require('node:assert')

const { isGatewayV1Event, isGatewayV2Event } = require('../../../lib/serverless/api-gateway')
const { gatewayV1Event, gatewayV2Event, lambaV1InvocationEvent } = require('./fixtures')
const {
restApiGatewayV1Event,
httpApiGatewayV1Event,
httpApiGatewayV2Event,
lambaV1InvocationEvent
} = require('./fixtures')

test('isGatewayV1Event', () => {
assert.equal(isGatewayV1Event(gatewayV1Event), true)
assert.equal(isGatewayV1Event(gatewayV2Event), false)
assert.equal(isGatewayV1Event(restApiGatewayV1Event), true)
assert.equal(isGatewayV1Event(httpApiGatewayV1Event), true)
assert.equal(isGatewayV1Event(httpApiGatewayV2Event), false)
assert.equal(isGatewayV1Event(lambaV1InvocationEvent), false)
})

test('isGatewayV2Event', () => {
assert.equal(isGatewayV2Event(gatewayV1Event), false)
assert.equal(isGatewayV2Event(gatewayV2Event), true)
assert.equal(isGatewayV2Event(restApiGatewayV1Event), false)
assert.equal(isGatewayV2Event(httpApiGatewayV1Event), false)
assert.equal(isGatewayV2Event(httpApiGatewayV2Event), true)
assert.equal(isGatewayV2Event(lambaV1InvocationEvent), false)
})

0 comments on commit f2e756e

Please sign in to comment.