From 569b6fd997b5ee2b03d00e997cd29f23e50a5aab Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Thu, 12 Oct 2017 16:59:17 -0400 Subject: [PATCH 1/6] adds support for multipart and x-www-form-urlencoded --- package.json | 1 + src/LambdaReq.js | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index be14449..800c072 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "sinon": "^2.0.0" }, "dependencies": { + "busboy": "^0.2.14", "debug": "^2.6.3" }, "nyc": { diff --git a/src/LambdaReq.js b/src/LambdaReq.js index 0fe41d3..e3327d9 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -1,5 +1,6 @@ import debug from 'debug' import LambdaReqError from './LambdaReqError' +import Busboy from 'busboy' const log = debug('LambdaReq:') @@ -72,7 +73,9 @@ class LambdaReq { this._event = event this._context = context this._callback = callback - + console.log(this.headers) + const busboy = new Busboy({ headers: this.headers }) + this._busboy = busboy console.assert(typeof event === 'object' && event !== null, 'Malformed Lambda event object.') console.assert(typeof callback === 'function', 'Malformed Lambda callback.') @@ -93,7 +96,10 @@ class LambdaReq { const reqData = { params: Object.assign({}, this.params), - headers: this.headers ? Object.assign({}, this.headers) : undefined + headers: this.headers ? Object.assign({}, this.headers) : undefined, + on: function(key, callback) { + busboy.on(key, callback) + } } let result @@ -103,8 +109,8 @@ class LambdaReq { log('handler %s responded with error: %s', this.currentRoute, err) return this._respond(err) } - if (result && result.then) { + this._busboy.end(this._event.body) log('handling an async result for %s', this.currentRoute) return result.then((res)=> this._respond(null, res)).catch((err)=> this._respond(err)) } else { @@ -126,9 +132,33 @@ class LambdaReq { this._routes[id] = handler } - _parseApiGatewayData (event = this._event) { + _parseApiGatewayBody(contentType = 'application/json', event) { const body = {} - Object.assign(body, JSON.parse(event.body)) + if (contentType.toLowerCase() === 'application/json') { + Object.assign(body, JSON.parse(event.body)); + } else if (contentType.toLowerCase() === 'application/x-www-form-urlencoded') { + const pieces = {}; + event.body.split('&').forEach(part => { + const keyValue = part.split('='); + pieces[keyValue[0]] = keyValue[1]; + }); + Object.assign(body, pieces); + // multipart is server on the busboy event + } else if (!/multipart/.test(contentType)) { + try { + Object.assign(body, JSON.parse(event.body)); + } catch(e) { + } + } + + return body; + } + + _parseApiGatewayData (event = this._event) { + const body = this._parseApiGatewayBody( + event.headers['content-type'] || event.headers['Content-Type'], + event + ); return { method: event.httpMethod, From eb996171aa078ab05a68ccdc252c0ecce65dd708 Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Thu, 12 Oct 2017 17:00:01 -0400 Subject: [PATCH 2/6] removes console.log --- src/LambdaReq.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LambdaReq.js b/src/LambdaReq.js index e3327d9..70e8fea 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -73,7 +73,6 @@ class LambdaReq { this._event = event this._context = context this._callback = callback - console.log(this.headers) const busboy = new Busboy({ headers: this.headers }) this._busboy = busboy console.assert(typeof event === 'object' && event !== null, 'Malformed Lambda event object.') From 2aa2dee13d54b4d1b8209a8edd9014b7ddc1322f Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Thu, 12 Oct 2017 17:02:42 -0400 Subject: [PATCH 3/6] linting --- src/LambdaReq.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/LambdaReq.js b/src/LambdaReq.js index 70e8fea..cd9e8f2 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -134,30 +134,31 @@ class LambdaReq { _parseApiGatewayBody(contentType = 'application/json', event) { const body = {} if (contentType.toLowerCase() === 'application/json') { - Object.assign(body, JSON.parse(event.body)); + Object.assign(body, JSON.parse(event.body)) } else if (contentType.toLowerCase() === 'application/x-www-form-urlencoded') { - const pieces = {}; + const pieces = {} event.body.split('&').forEach(part => { - const keyValue = part.split('='); - pieces[keyValue[0]] = keyValue[1]; - }); - Object.assign(body, pieces); + const keyValue = part.split('=') + pieces[keyValue[0]] = keyValue[1] + }) + Object.assign(body, pieces) // multipart is server on the busboy event } else if (!/multipart/.test(contentType)) { try { - Object.assign(body, JSON.parse(event.body)); + Object.assign(body, JSON.parse(event.body)) } catch(e) { + // } } - return body; + return body } _parseApiGatewayData (event = this._event) { const body = this._parseApiGatewayBody( event.headers['content-type'] || event.headers['Content-Type'], event - ); + ) return { method: event.httpMethod, From ab50c73f159c1ac375740f31330778feeb398c9c Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Sat, 14 Oct 2017 07:00:32 -0400 Subject: [PATCH 4/6] fixes unit tests --- src/LambdaReq.js | 38 +++++++++++++++++++++++++++----------- tests/unit/LambdaReq.js | 32 ++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/LambdaReq.js b/src/LambdaReq.js index cd9e8f2..076a50e 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -73,10 +73,21 @@ class LambdaReq { this._event = event this._context = context this._callback = callback - const busboy = new Busboy({ headers: this.headers }) - this._busboy = busboy + + let busboy + console.assert(typeof event === 'object' && event !== null, 'Malformed Lambda event object.') console.assert(typeof callback === 'function', 'Malformed Lambda callback.') + + + if (this.headers && this.headers['Content-Type']) { + if (/^multipart/.test(this.headers['Content-Type'])) { + busboy = new Busboy({ headers: this.headers }) + this._busboy = busboy + } + } + + log('handling invocation for route %s', this.currentRoute) @@ -96,11 +107,14 @@ class LambdaReq { const reqData = { params: Object.assign({}, this.params), headers: this.headers ? Object.assign({}, this.headers) : undefined, - on: function(key, callback) { - busboy.on(key, callback) + on: (key, callback) => { + if (busboy) { + busboy.on(key, callback) + } } } + let result try { result = this._routes[this.currentRoute](reqData, this) @@ -109,7 +123,9 @@ class LambdaReq { return this._respond(err) } if (result && result.then) { - this._busboy.end(this._event.body) + if (this._busboy) { + this._busboy.end(this._event.body) + } log('handling an async result for %s', this.currentRoute) return result.then((res)=> this._respond(null, res)).catch((err)=> this._respond(err)) } else { @@ -131,10 +147,12 @@ class LambdaReq { this._routes[id] = handler } - _parseApiGatewayBody(contentType = 'application/json', event) { + _parseApiGatewayBody(event) { const body = {} + const headers = event.headers || {} + const contentType = headers['content-type'] || headers['Content-Type'] || 'application/json' if (contentType.toLowerCase() === 'application/json') { - Object.assign(body, JSON.parse(event.body)) + Object.assign(body, JSON.parse(event.body || '{}')) } else if (contentType.toLowerCase() === 'application/x-www-form-urlencoded') { const pieces = {} event.body.split('&').forEach(part => { @@ -155,10 +173,8 @@ class LambdaReq { } _parseApiGatewayData (event = this._event) { - const body = this._parseApiGatewayBody( - event.headers['content-type'] || event.headers['Content-Type'], - event - ) + const headers = event.headers || {} + const body = this._parseApiGatewayBody(event) return { method: event.httpMethod, diff --git a/tests/unit/LambdaReq.js b/tests/unit/LambdaReq.js index f555c47..836ac26 100644 --- a/tests/unit/LambdaReq.js +++ b/tests/unit/LambdaReq.js @@ -36,10 +36,10 @@ describe('LambdaReq', () => { const lambda = new LambdaReq(API_GATEWAY_EVENT, {}, callback) lambda.get('/v1/test', handler) lambda.invoke() - should(handler.calledWith( + should(handler.calledWith(sinon.match( { params: { name: 'john', id: 'u-123', active: true }, headers: { 'Content-Type': 'application/json' } - }, + }), lambda )).eql(true) should(callback.getCall(0).args[1]).containEql({ @@ -48,6 +48,26 @@ describe('LambdaReq', () => { }) }) + it('responds to x-www-form-urlencoded', () => { + const callback = sinon.stub() + const handler = sinon.stub().returns({ success: true }) + const event = Object.assign({}, API_GATEWAY_EVENT, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: 'active=true' + }) + + const lambda = new LambdaReq(event, {}, callback) + lambda.get('/v1/test', handler) + lambda.invoke() + + should(callback.getCall(0).args[1]).containEql({ + statusCode: 200, + body: '{"success":true}' + }) + }) + describe('when the handler result is a promise', () => { it('waits for the promise to resolve then it responds', () => { const callback = sinon.stub() @@ -56,10 +76,10 @@ describe('LambdaReq', () => { lambda.get('/v1/test', handler) return lambda.invoke() .then(()=> { - should(handler.calledWith( + should(handler.calledWith(sinon.match( { params: { name: 'john', id: 'u-123', active: true }, headers: { 'Content-Type': 'application/json' } - }, + }), lambda )).eql(true) should(callback.getCall(0).args[1]).containEql({ @@ -175,8 +195,8 @@ describe('LambdaReq', () => { const lambda = new LambdaReq(PROXY_EVENT, {}, callback) lambda.proxy('commandName', handler) lambda.invoke() - should(handler.calledWith( - { params: { id: 'u-123' }, headers: undefined }, + should(handler.calledWith(sinon.match( + { params: { id: 'u-123' }, headers: undefined }), lambda )).eql(true) should(callback.getCall(0).args[1]).containEql('{"success":true}') From d48d589c8baee41cefddbb2d4554189250ae58fc Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Sun, 15 Oct 2017 10:06:23 -0400 Subject: [PATCH 5/6] removes busboy --- package.json | 1 - src/LambdaReq.js | 19 +------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/package.json b/package.json index 800c072..be14449 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "sinon": "^2.0.0" }, "dependencies": { - "busboy": "^0.2.14", "debug": "^2.6.3" }, "nyc": { diff --git a/src/LambdaReq.js b/src/LambdaReq.js index 076a50e..84570ec 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -79,15 +79,6 @@ class LambdaReq { console.assert(typeof event === 'object' && event !== null, 'Malformed Lambda event object.') console.assert(typeof callback === 'function', 'Malformed Lambda callback.') - - if (this.headers && this.headers['Content-Type']) { - if (/^multipart/.test(this.headers['Content-Type'])) { - busboy = new Busboy({ headers: this.headers }) - this._busboy = busboy - } - } - - log('handling invocation for route %s', this.currentRoute) @@ -107,14 +98,9 @@ class LambdaReq { const reqData = { params: Object.assign({}, this.params), headers: this.headers ? Object.assign({}, this.headers) : undefined, - on: (key, callback) => { - if (busboy) { - busboy.on(key, callback) - } - } + body: this._event.body } - let result try { result = this._routes[this.currentRoute](reqData, this) @@ -123,9 +109,6 @@ class LambdaReq { return this._respond(err) } if (result && result.then) { - if (this._busboy) { - this._busboy.end(this._event.body) - } log('handling an async result for %s', this.currentRoute) return result.then((res)=> this._respond(null, res)).catch((err)=> this._respond(err)) } else { From 7c86d25a3c80322faf85a53591a3fde8383b57b0 Mon Sep 17 00:00:00 2001 From: James Charlesworth Date: Sun, 15 Oct 2017 10:08:36 -0400 Subject: [PATCH 6/6] additional busboy removal --- src/LambdaReq.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/LambdaReq.js b/src/LambdaReq.js index 84570ec..d622273 100644 --- a/src/LambdaReq.js +++ b/src/LambdaReq.js @@ -1,7 +1,5 @@ import debug from 'debug' import LambdaReqError from './LambdaReqError' -import Busboy from 'busboy' - const log = debug('LambdaReq:') class LambdaReq { @@ -73,8 +71,6 @@ class LambdaReq { this._event = event this._context = context this._callback = callback - - let busboy console.assert(typeof event === 'object' && event !== null, 'Malformed Lambda event object.') console.assert(typeof callback === 'function', 'Malformed Lambda callback.') @@ -143,7 +139,6 @@ class LambdaReq { pieces[keyValue[0]] = keyValue[1] }) Object.assign(body, pieces) - // multipart is server on the busboy event } else if (!/multipart/.test(contentType)) { try { Object.assign(body, JSON.parse(event.body))