lambda-api
Advanced tools
Comparing version 0.6.0 to 0.7.0
55
index.js
@@ -1,2 +0,2 @@ | ||
'use strict'; | ||
'use strict' | ||
@@ -6,3 +6,3 @@ /** | ||
* @author Jeremy Daly <jeremy@jeremydaly.com> | ||
* @version 0.6.0 | ||
* @version 0.7.0 | ||
* @license MIT | ||
@@ -35,3 +35,5 @@ */ | ||
// Default callback | ||
this._cb = function(err,res) { console.log('No callback specified') } | ||
this._cb = function() { | ||
console.log('No callback specified') // eslint-disable-line no-console | ||
} | ||
@@ -111,3 +113,3 @@ // Middleware stack | ||
path: '/'+this._prefix.concat(parsedPath).join('/') } | ||
} : {}), | ||
} : {}), | ||
route.slice(0,i+1) | ||
@@ -130,3 +132,3 @@ ) | ||
this._event = event | ||
this._context = context | ||
this._context = this.context = context | ||
this._cb = cb | ||
@@ -147,5 +149,24 @@ | ||
if (response._state !== 'processing') break | ||
// Init for matching routes | ||
let matched = false | ||
// Test paths if they are supplied | ||
for (const path of mw[0]) { | ||
if ( | ||
path === request.path || // If exact path match | ||
path === request.route || // If exact route match | ||
// If a wildcard match | ||
(path.substr(-1) === '*' && new RegExp('^' + path.slice(0, -1) + '.*$').test(request.route)) | ||
) { | ||
matched = true | ||
break | ||
} | ||
} | ||
if (mw[0].length > 0 && !matched) continue | ||
// Promisify middleware | ||
await new Promise(r => { | ||
let rtn = mw(request,response,() => { r() }) | ||
let rtn = mw[1](request,response,() => { r() }) | ||
if (rtn) response.send(rtn) | ||
@@ -178,3 +199,3 @@ }) | ||
let message; | ||
let message | ||
@@ -184,6 +205,6 @@ if (e instanceof Error) { | ||
message = e.message | ||
!this._test && console.log(e) | ||
!this._test && console.log(e) // eslint-disable-line no-console | ||
} else { | ||
message = e | ||
!this._test && console.log('API Error:',e) | ||
!this._test && console.log('API Error:',e) // eslint-disable-line no-console | ||
} | ||
@@ -232,5 +253,9 @@ | ||
// Middleware handler | ||
use(fn) { | ||
use(path,handler) { | ||
let fn = typeof path === 'function' ? path : handler | ||
let routes = typeof path === 'string' ? Array.of(path) : (Array.isArray(path) ? path : []) | ||
if (fn.length === 3) { | ||
this._middleware.push(fn) | ||
this._middleware.push([routes,fn]) | ||
} else if (fn.length === 4) { | ||
@@ -261,4 +286,4 @@ this._errors.push(fn) | ||
setRoute(obj, value, path) { | ||
if (typeof path === "string") { | ||
let path = path.split('.') | ||
if (typeof path === 'string') { | ||
let path = path.split('.') | ||
} | ||
@@ -292,3 +317,3 @@ | ||
} catch(e) { | ||
console.error(e.message) | ||
console.error(e.message) // eslint-disable-line no-console | ||
} | ||
@@ -330,3 +355,3 @@ } | ||
if (format) { | ||
prettyPrint(routes) | ||
console.log(prettyPrint(routes)) // eslint-disable-line no-console | ||
} else { | ||
@@ -333,0 +358,0 @@ return routes |
@@ -35,3 +35,2 @@ 'use strict' | ||
xml: 'application/xml', | ||
xls: 'application/xml', | ||
@@ -38,0 +37,0 @@ // other binary |
@@ -10,15 +10,25 @@ 'use strict' | ||
module.exports = routes => { | ||
let out = '' | ||
// Calculate column widths | ||
let widths = routes.reduce((acc,row) => { | ||
return [Math.max(acc[0],row[0].length),Math.max(acc[1],row[1].length)] | ||
return [ | ||
Math.max(acc[0],Math.max(6,row[0].length)), | ||
Math.max(acc[1],Math.max(5,row[1].length)) | ||
] | ||
},[0,0]) | ||
console.log('╔══' + ''.padEnd(widths[0],'═') + '══╤══' + ''.padEnd(widths[1],'═') + '══╗') | ||
console.log('║ ' + "\u001b[1m" + 'METHOD'.padEnd(widths[0]) + "\u001b[0m" + ' │ ' + "\u001b[1m" + 'ROUTE'.padEnd(widths[1]) + "\u001b[0m" + ' ║') | ||
console.log('╟──' + ''.padEnd(widths[0],'─') + '──┼──' + ''.padEnd(widths[1],'─') + '──╢') | ||
out += '╔══' + ''.padEnd(widths[0],'═') + '══╤══' + ''.padEnd(widths[1],'═') + '══╗\n' | ||
out += '║ ' + '\u001b[1m' + 'METHOD'.padEnd(widths[0]) + '\u001b[0m' + ' │ ' + '\u001b[1m' + 'ROUTE'.padEnd(widths[1]) + '\u001b[0m' + ' ║\n' | ||
out += '╟──' + ''.padEnd(widths[0],'─') + '──┼──' + ''.padEnd(widths[1],'─') + '──╢\n' | ||
routes.forEach((route,i) => { | ||
console.log('║ ' + route[0].padEnd(widths[0]) + ' │ ' + route[1].padEnd(widths[1]) + ' ║') | ||
if (i < routes.length-1) { console.log('╟──' + ''.padEnd(widths[0],'─') + '──┼──' + ''.padEnd(widths[1],'─') + '──╢') } | ||
out += '║ ' + route[0].padEnd(widths[0]) + ' │ ' + route[1].padEnd(widths[1]) + ' ║\n' | ||
if (i < routes.length-1) { | ||
out += '╟──' + ''.padEnd(widths[0],'─') + '──┼──' + ''.padEnd(widths[1],'─') + '──╢\n' | ||
} // end if | ||
}) | ||
console.log('╚══' + ''.padEnd(widths[0],'═') + '══╧══' + ''.padEnd(widths[1],'═') + '══╝') | ||
out += '╚══' + ''.padEnd(widths[0],'═') + '══╧══' + ''.padEnd(widths[1],'═') + '══╝' | ||
return out | ||
} |
@@ -21,3 +21,5 @@ 'use strict' | ||
// Init and default the handler | ||
this._handler = function() { console.log('No handler specified') } | ||
this._handler = function() { | ||
console.log('No handler specified') // eslint-disable-line no-console | ||
} | ||
@@ -72,2 +74,8 @@ // Expose Namespaces | ||
// Parse id from context | ||
this.id = this.app.context.awsRequestId ? this.app.context.awsRequestId : null | ||
// Add context | ||
this.context = typeof this.app.context === 'object' ? this.app.context : {} | ||
// Capture the raw body | ||
@@ -80,3 +88,3 @@ this.rawBody = this.app._event.body | ||
// Set the body | ||
if (this.headers['content-type'] && this.headers['content-type'].includes("application/x-www-form-urlencoded")) { | ||
if (this.headers['content-type'] && this.headers['content-type'].includes('application/x-www-form-urlencoded')) { | ||
this.body = QS.parse(this.body) | ||
@@ -119,7 +127,7 @@ } else if (typeof this.body === 'object') { | ||
let route = routes['__'+this.method] ? routes['__'+this.method] : | ||
(routes['__ANY'] ? routes['__ANY'] : | ||
(wildcard && wildcard['__'+this.method] ? wildcard['__'+this.method] : | ||
(wildcard && wildcard['__ANY'] ? wildcard['__ANY'] : | ||
(this.method === 'HEAD' && routes['__GET'] ? routes['__GET'] : | ||
undefined)))) | ||
(routes['__ANY'] ? routes['__ANY'] : | ||
(wildcard && wildcard['__'+this.method] ? wildcard['__'+this.method] : | ||
(wildcard && wildcard['__ANY'] ? wildcard['__ANY'] : | ||
(this.method === 'HEAD' && routes['__GET'] ? routes['__GET'] : | ||
undefined)))) | ||
@@ -126,0 +134,0 @@ // Check for the requested method |
@@ -34,3 +34,3 @@ 'use strict' | ||
// Set the Content-Type by default | ||
"content-type": "application/json" //charset=UTF-8 | ||
'content-type': 'application/json' //charset=UTF-8 | ||
} | ||
@@ -132,3 +132,3 @@ | ||
// Set the name and value of the cookie | ||
let cookieString = (typeof name !== 'String' ? name.toString() : name) | ||
let cookieString = (typeof name !== 'string' ? name.toString() : name) | ||
+ '=' + encodeURIComponent(UTILS.encodeBody(value)) | ||
@@ -135,0 +135,0 @@ |
@@ -9,12 +9,12 @@ 'use strict' | ||
const QS = require('querystring') // Require the querystring library | ||
const crypto = require('crypto') // Require Node.js crypto library | ||
const QS = require('querystring') // Require the querystring library | ||
const crypto = require('crypto') // Require Node.js crypto library | ||
const entityMap = { | ||
"&": "&", | ||
"<": "<", | ||
">": ">", | ||
'"': '"', | ||
"'": ''' | ||
} | ||
const entityMap = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
'\'': ''' | ||
} | ||
@@ -46,3 +46,3 @@ module.exports.escapeHtml = html => html.replace(/[&<>"']/g, s => entityMap[s]) | ||
} catch(e) { | ||
return body; | ||
return body | ||
} | ||
@@ -56,10 +56,13 @@ } | ||
switch (type) { | ||
case 'Basic': | ||
case 'Basic': { | ||
let creds = Buffer.from(value, 'base64').toString().split(':') | ||
return { type, value, username: creds[0], password: creds[1] ? creds[1] : null } | ||
case 'OAuth': | ||
} | ||
case 'OAuth': { | ||
let params = QS.parse(value.replace(/",\s*/g,'&').replace(/"/g,'').trim()) | ||
return Object.assign({ type, value }, params) | ||
default: | ||
} | ||
default: { | ||
return { type, value } | ||
} | ||
} | ||
@@ -111,2 +114,2 @@ } | ||
module.exports.generateEtag = data => | ||
crypto.createHash('sha256').update(encodeBody(data)).digest("hex").substr(0,32) | ||
crypto.createHash('sha256').update(encodeBody(data)).digest('hex').substr(0,32) |
{ | ||
"name": "lambda-api", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Lightweight web framework for your serverless applications", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha" | ||
"test": "mocha --check-leaks --recursive", | ||
"test-cov": "nyc --reporter=lcov mocha --check-leaks --recursive", | ||
"test-ci": "nyc npm test && nyc report --reporter=text-lcov | ./node_modules/coveralls/bin/coveralls.js", | ||
"lint": "eslint ." | ||
}, | ||
@@ -36,5 +39,18 @@ "repository": { | ||
"chai": "^4.1.2", | ||
"coveralls": "^3.0.1", | ||
"eslint": "^4.19.1", | ||
"eslint-config-airbnb-base": "^12.1.0", | ||
"eslint-plugin-import": "^2.12.0", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^4.0.1", | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"nyc": "^11.8.0", | ||
"sinon": "^4.5.0" | ||
}, | ||
"files": [ | ||
"LICENSE", | ||
"README.md", | ||
"index.js", | ||
"lib/" | ||
], | ||
"engines": { | ||
@@ -41,0 +57,0 @@ "node": ">= 8.10.0" |
@@ -6,2 +6,3 @@ [](https://serverless-api.com/) | ||
[](https://www.npmjs.com/package/lambda-api) | ||
[](https://coveralls.io/github/jeremydaly/lambda-api?branch=master) | ||
@@ -113,2 +114,5 @@ ### Lightweight web framework for your serverless applications | ||
### v0.7: Restrict middleware execution to certain paths | ||
Middleware now supports an optional path parameter that supports multiple paths, wildcards, and parameter matching to better control middleware execution. See [middleware](#middleware) for more information. | ||
### v0.6: Support for both `callback-style` and `async-await` | ||
@@ -325,2 +329,3 @@ In additional to `res.send()`, you can now simply `return` the body from your route and middleware functions. See [Returning Responses](#returning-responses) for more information. | ||
- `version`: The version set at initialization | ||
- `id`: The awsRequestId from the Lambda `context` | ||
- `params`: Dynamic path parameters parsed from the path (see [path parameters](#path-parameters)) | ||
@@ -342,2 +347,3 @@ - `method`: The HTTP method of the request | ||
- `cookies`: An object containing cookies sent from the browser (see the [cookie](#cookiename-value-options) `RESPONSE` method) | ||
- `context`: Reference to the `context` passed into the Lambda handler function | ||
@@ -649,3 +655,3 @@ The request object can be used to pass additional information through the processing chain. For example, if you are using a piece of authentication middleware, you can add additional keys to the `REQUEST` object with information about the user. See [middleware](#middleware) for more information. | ||
## Middleware | ||
The API supports middleware to preprocess requests before they execute their matching routes. Middleware is defined using the `use` method and require a function with three parameters for the `REQUEST`, `RESPONSE`, and `next` callback. For example: | ||
The API supports middleware to preprocess requests before they execute their matching routes. Middleware is defined using the `use` method and requires a function with three parameters for the `REQUEST`, `RESPONSE`, and `next` callback. For example: | ||
@@ -675,4 +681,28 @@ ```javascript | ||
**NOTE:** Middleware can use either callbacks like `res.send()` or `return` to trigger a response to the user. Please note that calling either one of these from within a middleware function will terminate execution and return the response immediately. | ||
**NOTE:** Middleware can use either callbacks like `res.send()` or `return` to trigger a response to the user. Please note that calling either one of these from within a middleware function will return the response immediately. | ||
### Restricting middleware execution to certain path(s) | ||
By default, middleware will execute on every path. If you only need it to execute for specific paths, pass the path (or array of paths) as the first parameter to the `use` function. | ||
```javascript | ||
// Single path | ||
api.use('/users', (req,res,next) => { next() }) | ||
// Wildcard path | ||
api.use('/users/*', (req,res,next) => { next() }) | ||
// Multiple path | ||
api.use(['/users','/posts'], (req,res,next) => { next() }) | ||
// Parameterized paths | ||
api.use('/users/:userId',(req,res,next) => { next() }) | ||
// Multiple paths with parameters and wildcards | ||
api.use(['/comments','/users/:userId','/posts/*'],(req,res,next) => { next() }) | ||
``` | ||
Path matching checks both the supplied `path` and the defined `route`. This means that parameterized paths can be matched by either the parameter (e.g. `/users/:param1`) or by an exact matching path (e.g. `/users/123`). | ||
## Clean Up | ||
@@ -823,2 +853,2 @@ The API has a built-in clean up method called 'finally()' that will execute after all middleware and routes have been completed, but before execution is complete. This can be used to close database connections or to perform other clean up functions. A clean up function can be defined using the `finally` method and requires a function with two parameters for the REQUEST and the RESPONSE as its only argument. For example: | ||
## Contributions | ||
Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/lambda-api/issues) for suggestions and bug reports. | ||
Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/lambda-api/issues) for suggestions and bug reports or create a pull request. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
848
1
77529
12
10
868
1