Skip to content

Commit 13d6efc

Browse files
committed
add apig v2 payload support
1 parent c1fe83e commit 13d6efc

File tree

8 files changed

+157
-27
lines changed

8 files changed

+157
-27
lines changed

.travis.yml

Lines changed: 0 additions & 6 deletions
This file was deleted.

__tests__/requests.unit.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const api = require('../index')({ version: 'v1.0' })
88
/******************************************************************************/
99
api.get('/test/hello', function(req,res) {
1010
let request = Object.assign(req,{app:null})
11-
// console.log(JSON.stringify(request,null,2));
1211
res.cookie('test','value')
1312
res.cookie('test2','value2')
1413
res.status(200).json({ request })
@@ -27,9 +26,9 @@ api.get('/test/201', function(req,res) {
2726

2827
describe('Request Tests:', function() {
2928

30-
describe('API Gateway Proxy Event', function() {
29+
describe('API Gateway Proxy Event v1', function() {
3130
it('Standard event', async function() {
32-
let _event = require('./sample-event-apigateway1.json')
31+
let _event = require('./sample-event-apigateway-v1.json')
3332
let _context = require('./sample-context-apigateway1.json')
3433
let result = await new Promise(r => api.run(_event,_context,(e,res) => { r(res) }))
3534
let body = JSON.parse(result.body)
@@ -57,7 +56,7 @@ describe('Request Tests:', function() {
5756
})
5857

5958
it('Missing X-Forwarded-For (sourceIp fallback)', async function() {
60-
let _event = require('./sample-event-apigateway1.json')
59+
let _event = require('./sample-event-apigateway-v1.json')
6160
let _context = require('./sample-context-apigateway1.json')
6261
delete _event.headers['X-Forwarded-For'] // remove the header
6362
delete _event.multiValueHeaders['x-forwarded-for'] // remove the header
@@ -86,6 +85,37 @@ describe('Request Tests:', function() {
8685
})
8786
})
8887

88+
describe('API Gateway Proxy Event v2', function() {
89+
it('Standard event', async function() {
90+
let _event = require('./sample-event-apigateway-v2.json')
91+
let _context = require('./sample-context-apigateway1.json')
92+
let result = await new Promise(r => api.run(_event,_context,(e,res) => { r(res) }))
93+
let body = JSON.parse(result.body)
94+
// console.log(result);
95+
// console.log(body.request.multiValueHeaders);
96+
expect(result.cookies).toEqual(['test=value; Path=/','test2=value2; Path=/'])
97+
expect(body).toHaveProperty('request')
98+
expect(body.request.id).toBeDefined()
99+
expect(body.request.interface).toBe('apigateway')
100+
expect(body.request.userAgent).toBe('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48')
101+
expect(body.request).toHaveProperty('requestContext')
102+
expect(body.request.ip).toBe('192.168.100.1')
103+
expect(body.request.pathParameters).toEqual({ "proxy": "hello" })
104+
expect(body.request.stageVariables).toEqual({ "stageVarName": "stageVarValue" })
105+
expect(body.request.isBase64Encoded).toBe(false)
106+
expect(body.request.clientType).toBe('desktop')
107+
expect(body.request.clientCountry).toBe('US')
108+
expect(body.request.route).toBe('/test/hello')
109+
expect(body.request.query.qs1).toBe('foo')
110+
expect(body.request.query.qs2).toBe('foo,bar')
111+
expect(body.request.multiValueQuery.qs2).toEqual(['foo','bar'])
112+
expect(body.request.multiValueQuery.qs3).toEqual(['bat','baz'])
113+
expect(body.request.headers['test-header']).toBe('val1,val2')
114+
expect(body.request.multiValueHeaders['test-header']).toEqual(['val1','val2'])
115+
expect(body.request.cookies).toEqual({ cookie1: 'test', cookie2: 123 })
116+
})
117+
})
118+
89119
describe('ALB Event', function() {
90120
it('Standard event', async function() {
91121
let _event = require('./sample-event-alb1.json')
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"version": "2.0",
3+
"routeKey": "$default",
4+
"rawPath": "/test/hello",
5+
"rawQueryString": "parameter1=value1&parameter1=value2&parameter2=value",
6+
"cookies": [
7+
"cookie1=test",
8+
"cookie2=123"
9+
],
10+
"headers": {
11+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
12+
"Accept-Encoding": "gzip, deflate, lzma, sdch, br",
13+
"Accept-Language": "en-US,en;q=0.8",
14+
"CloudFront-Forwarded-Proto": "https",
15+
"CloudFront-Is-Desktop-Viewer": "true",
16+
"CloudFront-Is-Mobile-Viewer": "false",
17+
"CloudFront-Is-SmartTV-Viewer": "false",
18+
"CloudFront-Is-Tablet-Viewer": "false",
19+
"CloudFront-Viewer-Country": "US",
20+
"Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
21+
"Upgrade-Insecure-Requests": "1",
22+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
23+
"Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
24+
"X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
25+
"X-Forwarded-For": "192.168.100.1, 192.168.1.1",
26+
"X-Forwarded-Port": "443",
27+
"X-Forwarded-Proto": "https",
28+
"test-header": "val1,val2"
29+
},
30+
"queryStringParameters": {
31+
"qs1": "foo",
32+
"qs2": "foo,bar",
33+
"qs3": "bat,baz"
34+
},
35+
"requestContext": {
36+
"accountId": "123456789012",
37+
"apiId": "api-id",
38+
"authentication": {
39+
"clientCert": {
40+
"clientCertPem": "CERT_CONTENT",
41+
"subjectDN": "www.example.com",
42+
"issuerDN": "Example issuer",
43+
"serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1",
44+
"validity": {
45+
"notBefore": "May 28 12:30:02 2019 GMT",
46+
"notAfter": "Aug 5 09:36:04 2021 GMT"
47+
}
48+
}
49+
},
50+
"authorizer": {
51+
"jwt": {
52+
"claims": {
53+
"claim1": "value1",
54+
"claim2": "value2"
55+
},
56+
"scopes": [
57+
"scope1",
58+
"scope2"
59+
]
60+
}
61+
},
62+
"domainName": "id.execute-api.us-east-1.amazonaws.com",
63+
"domainPrefix": "id",
64+
"http": {
65+
"method": "GET",
66+
"path": "/test/hello",
67+
"protocol": "HTTP/1.1",
68+
"sourceIp": "IP",
69+
"userAgent": "agent"
70+
},
71+
"requestId": "id",
72+
"routeKey": "$default",
73+
"stage": "$default",
74+
"time": "12/Mar/2020:19:03:58 +0000",
75+
"timeEpoch": 1583348638390
76+
},
77+
"body": "Hello from Lambda",
78+
"pathParameters": {
79+
"proxy": "hello"
80+
},
81+
"isBase64Encoded": false,
82+
"stageVariables": {
83+
"stageVarName": "stageVarValue"
84+
}
85+
}

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* Lightweight web framework for your serverless applications
55
* @author Jeremy Daly <[email protected]>
6-
* @version 0.10.8
6+
* @version 0.11.0
77
* @license MIT
88
*/
99

lib/request.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,19 @@ class REQUEST {
6161
// Parse the request
6262
async parseRequest() {
6363

64+
// Set the payload version
65+
this.payloadVersion = this.app._event.version ? this.app._event.version : null
66+
6467
// Detect multi-value support
6568
this._multiValueSupport = 'multiValueHeaders' in this.app._event
6669

6770
// Set the method
68-
this.method = this.app._event.httpMethod ? this.app._event.httpMethod.toUpperCase() : 'GET'
71+
this.method = this.app._event.httpMethod ? this.app._event.httpMethod.toUpperCase()
72+
: this.app._event.requestContext && this.app._event.requestContext.http ? this.app._event.requestContext.http.method.toUpperCase()
73+
: 'GET'
6974

7075
// Set the path
71-
this.path = this.app._event.path
76+
this.path = this.payloadVersion === '2.0' ? this.app._event.rawPath : this.app._event.path
7277

7378
// Set the query parameters (backfill for ALB)
7479
this.query = Object.assign({}, this.app._event.queryStringParameters,
@@ -82,7 +87,7 @@ class REQUEST {
8287
// Set the multi-value query parameters (simulate if no multi-value support)
8388
this.multiValueQuery = Object.assign({},
8489
this._multiValueSupport ? {} : Object.keys(this.query)
85-
.reduce((qs,key) => Object.assign(qs, { [key]: [this.query[key]] }), {}),
90+
.reduce((qs,key) => Object.assign(qs, { [key]: this.query[key].split(',') }), {}),
8691
this.app._event.multiValueQueryStringParameters)
8792

8893
// Set the raw headers (normalize multi-values)
@@ -98,21 +103,21 @@ class REQUEST {
98103

99104
this.multiValueHeaders = this._multiValueSupport ? this.app._event.multiValueHeaders
100105
: Object.keys(this.headers).reduce((headers,key) =>
101-
Object.assign(headers,{ [key.toLowerCase()]: [this.headers[key]] }),{})
106+
Object.assign(headers,{ [key.toLowerCase()]: this.headers[key].split(',') }),{})
102107

103108
// Extract user agent
104109
this.userAgent = this.headers['user-agent']
105110

111+
// Get cookies from event
112+
let cookies = this.app._event.cookies ? this.app._event.cookies
113+
: this.headers.cookie ? this.headers.cookie.split(';')
114+
: []
115+
106116
// Set and parse cookies
107-
this.cookies = this.headers.cookie ?
108-
this.headers.cookie.split(';')
109-
.reduce(
110-
(acc,cookie) => {
111-
cookie = cookie.trim().split('=')
112-
return Object.assign(acc,{ [cookie[0]] : UTILS.parseBody(decodeURIComponent(cookie[1])) })
113-
},
114-
{}
115-
) : {}
117+
this.cookies = cookies.reduce((acc,cookie) => {
118+
cookie = cookie.trim().split('=')
119+
return Object.assign(acc,{ [cookie[0]] : UTILS.parseBody(decodeURIComponent(cookie[1])) })
120+
},{})
116121

117122
// Attempt to parse the auth
118123
this.auth = UTILS.parseAuth(this.headers.authorization)

lib/response.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,26 @@ class RESPONSE {
457457
body = ''
458458
}
459459

460+
let headers = {}
461+
let cookies = {}
462+
463+
if (this._request.payloadVersion === '2.0') {
464+
if (this._headers['set-cookie']) {
465+
cookies = { cookies: this._headers['set-cookie'] }
466+
delete this._headers['set-cookie']
467+
}
468+
}
469+
470+
if (this._request._multiValueSupport) {
471+
headers = { multiValueHeaders: this._headers }
472+
} else {
473+
headers = { headers: UTILS.stringifyHeaders(this._headers) }
474+
}
475+
460476
// Create the response
461477
this._response = Object.assign({},
462-
this._request._multiValueSupport ? { multiValueHeaders: this._headers }
463-
: { headers: UTILS.stringifyHeaders(this._headers) },
478+
headers,
479+
cookies,
464480
{
465481
statusCode: this._statusCode,
466482
body: this._request.method === 'HEAD' ? '' : UTILS.encodeBody(body,this._serializer),

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lambda-api",
3-
"version": "0.10.8",
3+
"version": "0.11.0",
44
"description": "Lightweight web framework for your serverless applications",
55
"main": "index.js",
66
"types": "index.d.ts",

0 commit comments

Comments
 (0)