Comparing version 0.7.0 to 0.7.1
@@ -31,14 +31,14 @@ // Load modules | ||
var tailId1 = request.addTail('tail1'); | ||
var tail1 = request.addTail('tail1'); | ||
setTimeout(function () { | ||
console.log(1); | ||
request.removeTail(tailId1); | ||
request.removeTail(tail1); // Using removeTail() interface | ||
}, 5000); | ||
var tailId2 = request.addTail('tail2'); | ||
var tail2 = request.addTail('tail2'); | ||
setTimeout(function () { | ||
console.log(2); | ||
request.removeTail(tailId2); | ||
tail2(); // Using tail() function interface | ||
}, 2000); | ||
@@ -45,0 +45,0 @@ |
@@ -8,2 +8,3 @@ { | ||
"main": "server", | ||
"private": "true", | ||
"engines": { | ||
@@ -10,0 +11,0 @@ "node": ">=0.8.0" |
@@ -31,3 +31,3 @@ /** | ||
var config = { docs: true }; | ||
var config = { name: 'Example', docs: true }; | ||
@@ -47,13 +47,10 @@ // Create Hapi servers | ||
{ method: 'GET', path: '/simple', config: { handler: internals.get, query: { input: S().min(3) } } }, | ||
{ method: 'GET', path: '/users/:id', config: { handler: internals.get, query: { name: S().description('the user name')} } } | ||
{ method: 'GET', path: '/users/:id', config: { description: 'Get a user', handler: internals.get, query: { name: S().description('the user name').required() } } } | ||
]); | ||
var schema = { | ||
title: S(), | ||
title: S().invalid('director'), | ||
status: S().valid('open', 'pending', 'close'), | ||
participants: A().includes(S(), N()) | ||
} | ||
// console.log("schema", sys.inspect(s)); | ||
// console.log(s.input.__validators[1].toString()) | ||
// console.log(sys.inspect(s.input.__validators.map(function(d){ return d.toString();}))); | ||
}; | ||
@@ -60,0 +57,0 @@ http.addRoute({ |
@@ -49,26 +49,26 @@ /* | ||
// Check if cachable | ||
// Validate matching rule | ||
if (config.isCached !== false) { // Defaults to true | ||
rule.isCached = true; | ||
// Validate matching rule | ||
if (isSet) { | ||
if (config.match) { | ||
if (config.match instanceof RegExp) { | ||
rule.match = config.match; | ||
} | ||
else { | ||
return new Error('Bad match value'); | ||
} | ||
if (isSet) { | ||
if (config.match) { | ||
if (config.match instanceof RegExp) { | ||
rule.match = config.match; | ||
} | ||
else { | ||
return new Error('Missing match'); | ||
return new Error('Bad match value'); | ||
} | ||
} | ||
else if (config.match) { | ||
return new Error('Single rule cannot contain match'); | ||
else { | ||
return new Error('Missing match'); | ||
} | ||
} | ||
else if (config.match) { | ||
return new Error('Single rule cannot contain match'); | ||
} | ||
// Check if cachable | ||
if (config.isCached !== false) { // Defaults to true | ||
rule.isCached = true; | ||
// Validate expiration settings | ||
@@ -75,0 +75,0 @@ |
@@ -82,3 +82,4 @@ // Declare internals | ||
debug: false, // Debugging interface (defaults: exports.debug) | ||
docs: false // Documentation generator (defaults: exports.docs) | ||
docs: false, // Documentation generator (defaults: exports.docs) | ||
batch: false // Batch interface (defaults: exports.batch) | ||
}; | ||
@@ -144,3 +145,11 @@ | ||
docsEndpoint: '/docs', | ||
templatePath: __dirname + '/templates/route.html' | ||
indexTemplatePath: __dirname + '/templates/index.html', | ||
routeTemplatePath: __dirname + '/templates/route.html' | ||
}; | ||
// Batch interface | ||
exports.batch = { | ||
batchEndpoint: '/batch' | ||
}; |
109
lib/docs.js
// Load modules | ||
var Utils = require('./utils'); | ||
var Err = require('./error'); | ||
var Types = require('joi').Types; | ||
@@ -16,9 +17,15 @@ var Fs = require('fs'); | ||
Utils.assert(config.template || config.templatePath, 'Config need to specify either a template or a templatePath'); | ||
Utils.assert(config.indexTemplate || config.indexTemplatePath, 'Config needs to specify either a indexTemplate or a indexTemplatePath'); | ||
Utils.assert(config.routeTemplate || config.routeTemplatePath, 'Config needs to specify either a routeTemplate or a routeTemplatePath'); | ||
var templateSource = config.template || Fs.readFileSync(config.templatePath, 'utf8'); | ||
internals.compiledTemplate = Handlebars.compile(templateSource); | ||
var indexTemplateSource = config.indexTemplate || Fs.readFileSync(config.indexTemplatePath, 'utf8'); | ||
var routeTemplateSource = config.routeTemplate || Fs.readFileSync(config.routeTemplatePath, 'utf8'); | ||
internals.compiledIndexTemplate = Handlebars.compile(indexTemplateSource); | ||
internals.compiledRouteTemplate = Handlebars.compile(routeTemplateSource); | ||
internals.config = config; | ||
delete internals.config.template; | ||
delete internals.config.indexTemplate; | ||
delete internals.config.routeTemplate; | ||
@@ -31,3 +38,3 @@ return { | ||
internals.generateMarkup = function(routes, path) { | ||
internals.generateIndexMarkup = function(routes, serverName) { | ||
@@ -37,5 +44,16 @@ Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array'); | ||
var templateData = internals.getRoutesData(routes); | ||
templateData.serverName = serverName; | ||
return internals.compiledIndexTemplate(templateData); | ||
}; | ||
internals.generateRoutesMarkup = function(routes, path) { | ||
Utils.assert(Object.prototype.toString.call(routes) === '[object Array]', 'routes must be an array'); | ||
var templateData = internals.getRoutesData(routes); | ||
templateData.path = path; | ||
return internals.compiledTemplate(templateData); | ||
return internals.compiledRouteTemplate(templateData); | ||
}; | ||
@@ -48,3 +66,3 @@ | ||
handler: internals.handler, | ||
query: { path: Types.String().required() } | ||
query: { path: Types.String() } | ||
}; | ||
@@ -57,16 +75,46 @@ }; | ||
var path = request.query.path; | ||
var routes = internals.findRoutes(request.server, path); | ||
var markup = internals.generateMarkup(routes, path); | ||
request.reply(markup); | ||
if (path) { | ||
request.reply(internals.getRoutesResponse(request.server, path)); | ||
} | ||
else { | ||
request.reply(internals.getIndexResponse(request.server)); | ||
} | ||
}; | ||
internals.getIndexResponse = function(server) { | ||
var routes = [].concat(server._routes.post, server._routes.get); | ||
routes = routes.filter(function(route) { | ||
return route !== null && route.path !== internals.config.docsEndpoint; | ||
}); | ||
routes.sort(function(route1, route2) { | ||
return route1.path > route2.path; | ||
}); | ||
return internals.generateIndexMarkup(routes, server.settings.name); | ||
}; | ||
internals.getRoutesResponse = function(server, path) { | ||
var routes = internals.findRoutes(server, path); | ||
return routes ? internals.generateRoutesMarkup(routes, path) : Err.notFound('No such path found'); | ||
}; | ||
internals.findRoutes = function (server, path) { | ||
var routes = []; | ||
routes.push(server._match('get', path)); | ||
routes.push(server._match('post', path)); | ||
return routes; | ||
routes = routes.filter(function(route) { | ||
return route !== null; | ||
}); | ||
return routes.length > 0 ? routes : null; | ||
}; | ||
@@ -82,3 +130,2 @@ | ||
var routeData = internals.getRouteData(routes[i]); | ||
if (routeData) { | ||
@@ -116,2 +163,6 @@ routesData.push(routeData); | ||
if (typeof params !== 'object') { | ||
return paramsData; | ||
} | ||
var keys = Object.keys(params); | ||
@@ -123,2 +174,4 @@ | ||
var param = params[key]; | ||
var paramValue = param.valueOf(); | ||
paramsData.push({ | ||
@@ -129,3 +182,6 @@ name: key, | ||
tags: typeof param.tags === 'function' ? '' : param.tags, | ||
type: param.type | ||
type: param.type, | ||
required: paramValue.__modifiers.some(internals.isRequiredParam), | ||
allowedValues: internals.getExistsValues(paramValue.__valids._exists), | ||
disallowedValues: internals.getExistsValues(paramValue.__invalids._exists) | ||
}); | ||
@@ -136,2 +192,27 @@ } | ||
return paramsData; | ||
}; | ||
internals.getExistsValues = function(exists) { | ||
var values = []; | ||
if (exists === null || exists === undefined) { | ||
return values; | ||
} | ||
var keys = Object.keys(exists); | ||
keys.forEach(function(key) { | ||
key = key.substring(1, key.length - 1); | ||
if (key !== 'ndefine' && key !== 'ul' && key.length !== 0) { | ||
values.push(key); | ||
} | ||
}); | ||
return values; | ||
}; | ||
internals.isRequiredParam = function(element) { | ||
return element === 'required'; | ||
}; |
@@ -32,3 +32,11 @@ // Declare internals | ||
return exports.create(message, 500, 'Internal error', { data: data, stack: internals.callStack() }); | ||
var custom = { | ||
trace: internals.callStack(1) | ||
}; | ||
if (data) { | ||
custom.data = data; | ||
} | ||
return exports.create(message, 500, 'Internal error', custom); | ||
}; | ||
@@ -59,3 +67,4 @@ | ||
var err = new Error(message || ''); | ||
var err = new Error(); | ||
err.message = message; | ||
err.code = code; | ||
@@ -74,3 +83,3 @@ err.text = text; | ||
internals.callStack = function () { | ||
internals.callStack = function (slice) { | ||
@@ -81,3 +90,4 @@ try { | ||
catch (e) { | ||
return e.stack; | ||
var stack = e.stack.replace(/ at /g, '').split('\n'); | ||
return stack.slice(2 + (slice ? slice : 0)); | ||
} | ||
@@ -84,0 +94,0 @@ }; |
@@ -29,2 +29,4 @@ // Load modules | ||
Utils.assert(this.constructor === internals.Monitor, 'Monitor must be instantiated using new'); | ||
this.server = server; | ||
@@ -31,0 +33,0 @@ this.settings = Utils.clone(this.server.settings.monitor) || {}; |
@@ -19,4 +19,6 @@ // Load modules | ||
*/ | ||
OSMonitor = function () { | ||
module.exports.Monitor = OSMonitor = function () { | ||
Utils.assert(this.constructor === OSMonitor, 'OSMonitor must be instantiated using new'); | ||
this.builtins = ['loadavg', 'uptime', 'freemem', 'totalmem', 'cpus']; | ||
@@ -51,3 +53,3 @@ | ||
* @param {Function} callback function to process the asynchronous result | ||
* @api private | ||
* @api private | ||
*/ | ||
@@ -57,50 +59,45 @@ OSMonitor.prototype.poll_cpu = function (target, callback) { | ||
var statfile = '/proc/stat'; | ||
try { | ||
Fs.readFile(statfile, function (err, contents) { | ||
Fs.readFile(statfile, function (err, contents) { | ||
if (err) { | ||
throw err; | ||
} | ||
if (err) { | ||
return callback(err); | ||
} | ||
// TODO: FUTURE: optimization for later, if target known, customize regexp for that | ||
var pattern = /cpu[\d]?[\s]+(.*)/g; | ||
var file_contents = contents.toString(); | ||
// TODO: FUTURE: optimization for later, if target known, customize regexp for that | ||
var pattern = /cpu[\d]?[\s]+(.*)/g; | ||
var file_contents = contents.toString(); | ||
var result; | ||
var cpulines = {}; | ||
while ((result = pattern.exec(file_contents)) !== null) { | ||
var source = result[0].split(/\s+/); | ||
var cpu = source.shift(); // remove 'cpu(\d?)' from string | ||
var line = source.map(function (d) { | ||
var result; | ||
var cpulines = {}; | ||
while ((result = pattern.exec(file_contents)) !== null) { | ||
var source = result[0].split(/\s+/); | ||
var cpu = source.shift(); // remove 'cpu(\d?)' from string | ||
var line = source.map(function (d) { | ||
return +d; | ||
}); // convert all to Number | ||
return +d; | ||
}); // convert all to Number | ||
// line = line.slice(0, 4); // strip non-relevant numbers | ||
cpulines[cpu] = line; | ||
// line = line.slice(0, 4); // strip non-relevant numbers | ||
cpulines[cpu] = line; | ||
if (target === cpu) { | ||
break; // short circuit if found | ||
} | ||
if (target === cpu) { | ||
break; // short circuit if found | ||
} | ||
} | ||
if (!cpulines.hasOwnProperty(target)) { | ||
return callback('No such target found for Monitor.poll_cpu (' + target + ' does not exist)'); | ||
} | ||
if (!cpulines.hasOwnProperty(target)) { | ||
return callback('No such target found for Monitor.poll_cpu (' + target + ' does not exist)'); | ||
} | ||
var cpuline = cpulines[target]; | ||
var cpustats = { | ||
idle: cpuline[3], | ||
total: cpuline.reduce(function (a, b) { | ||
var cpuline = cpulines[target]; | ||
var cpustats = { | ||
idle: cpuline[3], | ||
total: cpuline.reduce(function (a, b) { | ||
return a + b; | ||
}) | ||
}; | ||
return a + b; | ||
}) | ||
}; | ||
return callback(null, cpustats); | ||
}); | ||
} | ||
catch (err) { | ||
return callback(err); | ||
} | ||
return callback(null, cpustats); | ||
}); | ||
}; | ||
@@ -111,3 +108,3 @@ | ||
* Return 1-second slice of total cpu usage percentage from across all cores | ||
* | ||
* | ||
* @param {Function} callback function to handle response | ||
@@ -163,4 +160,4 @@ * @api public | ||
ChildProcess.exec('df -m ' + filesystem + ' | tail -1 | tr -s " "', function (err, stdout, stderr) { | ||
ChildProcess.exec('df -m ' + filesystem, function (err, stdout, stderr) { | ||
if (err || | ||
@@ -171,29 +168,18 @@ stderr !== '') { | ||
} | ||
var values = stdout.replace(/^\s+/g, '').split(' ') | ||
if (isNaN(parseInt(values[0]))) { | ||
var total = values[1]; | ||
var used = values[2]; | ||
var lines = stdout.split("\n"); | ||
var dfInfo = lines[1].replace(/[\s\n\r]+/g, " ").split(" "); | ||
var output = { | ||
total: parseInt(dfInfo[1]), | ||
free: parseInt(dfInfo[3]) | ||
} | ||
else { | ||
var total = values[0]; | ||
var used = values[1] | ||
if (output.total < output.free) { | ||
return callback("system reports total disk space less than free disk space"); | ||
} | ||
var output = { | ||
total: parseInt(total), | ||
used: parseInt(used) | ||
}; | ||
return callback(null, output); | ||
// return callback(null, stdout.replace(/\s/g, '').replace('%', '')); | ||
}); | ||
}; | ||
module.exports = new OSMonitor(); | ||
module.exports.Monitor = OSMonitor; | ||
}; |
@@ -20,3 +20,3 @@ // Load modules | ||
return next(); | ||
}; | ||
} | ||
@@ -23,0 +23,0 @@ // Levels are: 'stream', 'raw', 'parse' |
@@ -27,2 +27,5 @@ // Load modules | ||
Utils.assert(this.constructor === internals.Request, 'Request must be instantiated using new'); | ||
Utils.assert(server, 'server must be provided'); | ||
Utils.assert(req, 'req must be provided'); | ||
Utils.assert(res, 'res must be provided'); | ||
@@ -109,2 +112,4 @@ // Register as event emitter | ||
Utils.assert(url, 'url must be provided'); | ||
this.url = Url.parse(url, true); | ||
@@ -118,2 +123,3 @@ this.path = this.url.pathname; // pathname excludes query | ||
Utils.assert(method, 'method must be provided'); | ||
this.method = method.toLowerCase(); | ||
@@ -148,2 +154,25 @@ }; | ||
internals.Request.prototype.getLog = function (tags) { | ||
if (tags === undefined) { // Other falsy values are legal tags | ||
return this._log; | ||
} | ||
var filter = Utils.mapToObject(tags instanceof Array ? tags : [tags]); | ||
var result = []; | ||
for (var i = 0, il = this._log.length; i < il; ++i) { | ||
var event = this._log[i]; | ||
for (var t = 0, tl = event.tags.length; t < tl; ++t) { | ||
var tag = event.tags[t]; | ||
if (filter[tag]) { | ||
result.push(event); | ||
} | ||
} | ||
} | ||
return result; | ||
}; | ||
internals.Request.prototype._onRequestExt = function (func, callback) { | ||
@@ -488,2 +517,4 @@ | ||
var self = this; | ||
name = name || 'unknown'; | ||
@@ -493,22 +524,31 @@ var tailId = this._tailIds++; | ||
this.log(['tail', 'add'], { name: name, id: tailId }); | ||
return tailId; | ||
}; | ||
var drop = function () { | ||
internals.Request.prototype.removeTail = function (tailId) { | ||
if (!self._tails[tailId]) { | ||
// Already removed | ||
self.log(['tail', 'remove', 'error'], { name: name, id: tailId }); | ||
return; | ||
} | ||
var name = this._tails[tailId]; | ||
if (name) { | ||
this.log(['tail', 'remove'], { name: name, id: tailId }); | ||
delete this._tails[tailId]; | ||
delete self._tails[tailId]; | ||
self.log(['tail', 'remove'], { name: name, id: tailId }); | ||
if (Object.keys(this._tails).length === 0 && | ||
this._isWagging) { | ||
if (Object.keys(self._tails).length === 0 && | ||
self._isWagging) { | ||
this.server.emit('tail', this); | ||
self.server.emit('tail', self); | ||
} | ||
} | ||
}; | ||
return drop; | ||
}; | ||
internals.Request.prototype.removeTail = function (dropFunc) { | ||
dropFunc(); | ||
}; | ||
internals.Request.prototype._wagTail = function () { | ||
@@ -515,0 +555,0 @@ |
@@ -20,2 +20,3 @@ // Load modules | ||
var Docs = require('./docs'); | ||
var Batch = require('./batch'); | ||
@@ -35,2 +36,4 @@ | ||
Utils.assert(this.constructor === internals.Server, 'Server must be instantiated using new'); | ||
Utils.assert(host, 'Host must be provided'); | ||
Utils.assert(port, 'Port must be provided'); | ||
@@ -158,2 +161,14 @@ // Register as event emitter | ||
// Setup batch endpoint | ||
if (this.settings.batch) { | ||
this.settings.batch = Utils.applyToDefaults(Defaults.batch, (typeof this.settings.batch === 'boolean' ? {} : this.settings.batch)); | ||
this.addRoute({ | ||
method: 'POST', | ||
path: this.settings.batch.batchEndpoint, | ||
config: Batch.config | ||
}); | ||
} | ||
return this; | ||
@@ -201,2 +216,5 @@ }; | ||
Utils.assert(method, 'The method parameter must be provided'); | ||
Utils.assert(path, 'The path parameter must be provided'); | ||
// Lookup route | ||
@@ -287,2 +305,3 @@ | ||
Utils.assert(routes, 'Routes parameter must exist'); | ||
for (var i = 0, il = routes.length; i < il; ++i) { | ||
@@ -289,0 +308,0 @@ this.addRoute(routes[i]); |
@@ -5,6 +5,7 @@ { | ||
"homepage": "http://hapijs.com", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)", | ||
"contributors":[ | ||
"Van Nguyen <the.gol.effect@gmail.com>" | ||
"Van Nguyen <the.gol.effect@gmail.com>", | ||
"Wyatt Preul <wpreul@gmail.com>" | ||
], | ||
@@ -11,0 +12,0 @@ "repository": "git://github.com/walmartlabs/hapi", |
@@ -31,2 +31,3 @@ ![hapi Logo](https://raw.github.com/walmartlabs/hapi/master/images/hapi.png) | ||
- [CORS](#cors) | ||
- [Batch](#batch) | ||
<p></p> | ||
@@ -299,3 +300,3 @@ - [**Server Events**](#server-events) | ||
request. The server will use WebSocket to stream the subscribed request logs to the web page in real-time. In application using multiple server instances, | ||
only one can enable the debug interface using the default port. To enable the debug console, set the `debug` option to _true_ or to an object with custom | ||
only one can enable the debug interface using the default port. To enable the debug console set the `debug` option to _true_ or to an object with custom | ||
configuration: | ||
@@ -310,10 +311,13 @@ - `websocketPort` - the port used by the WebSocket connection. Defaults to _3000_. | ||
In order to make it easy to generate documentation for the routes you add to **hapi**, a documentation generator is provided. By default the documentation generator is turned _off_. | ||
To enable the docs endpoint you can pass the following options to the **hapi** `server` under the `docs` setting name: | ||
In order to make it easy to generate documentation for the routes you add to **hapi**, a documentation generator is provided. By default the documentation | ||
generator is turned _off_. To enable the docs endpoint set the `docs` option to _true_ or to an object with custom configuration: | ||
- `docsEndpoint` - the path where the documentation will be served from. Default is '/docs'. | ||
- `templatePath` - the file path where the template file is located. Default is 'lib/templates/route.html'. | ||
- `template` - the raw source of a template to use. If `template` is provided then it will be used over the file located at `templatePath`. | ||
- `indexTemplatePath` - the file path where the index template file is located. Default is 'lib/templates/index.html'. | ||
- `indexTemplate` - the raw source of a index template to use. If `indexTemplate` is provided then it will be used over the file located at `indexTemplatePath`. | ||
- `routeTemplatePath` - the file path where the routes template file is located. Default is 'lib/templates/route.html'. | ||
- `routeTemplate` - the raw source of a route template to use. If `routeTemplate` is provided then it will be used over the file located at `routeTemplatePath`. | ||
- `templateParams` - an optional object of any extra information you want to pass into your template, this will be located in the templateParams object in the template data object. | ||
By default there is an index page that lists all of the available routes configured in **hapi** that is located at the `docsEndpoint`. From this page users are able to navigate to individual routes to read the related documentation. | ||
### CORS | ||
@@ -333,2 +337,11 @@ | ||
### Batch | ||
The batch endpoint makes it easy to combine requests into a single one. It also supports pipelining so you are able to take the result of one of the endpoints in the batch request and use it in a subsequent endpoint. The batch endpoint only responds to POST requests. | ||
By default the batch endpoint is turned _off_. To enable the batch endpoint set the `batch` option to _true_ or to an object with the following custom configuration: | ||
- `batchEndpoint` - the path where batch requests will be served from. Default is '/batch'. | ||
As an example to help explain the use of the endpoint, assume that the server has a route at '/currentuser' and '/users/:id/profile/'. You can make a POST request to the batch endpoint with the following body: | ||
`{ "GET": [ "/currentuser", "/users/$0.id/profile ] }` and it will return an array with the current user and their profile. | ||
## Server Events | ||
@@ -448,7 +461,36 @@ | ||
The request object is also decorated with the _'log(tags, [data, timestamp])'_ which adds a record to the request log where: | ||
- _'tags'_ - a single string or an array of strings (e.g. _['error', 'database', 'read']_) used to identify the logged event. Tags are used instead of log levels and provide a much more expressive mechanism for describing and filtering events. | ||
- _'data'_ - an optional message string or object with the application data being logged. | ||
- _'timestamp'_ - an optional timestamp override (if not present, the server will use current time), expressed in milliseconds since 1970 (_new Date().getTime()_). | ||
The request object is also decorated with the following methods. | ||
- _'log(tags, [data, timestamp])'_ which adds a record to the request log where: | ||
- _'tags'_ - a single string or an array of strings (e.g. _['error', 'database', 'read']_) used to identify the logged event. Tags are used instead of log levels and provide a much more expressive mechanism for describing and filtering events. | ||
- _'data'_ - an optional message string or object with the application data being logged. | ||
- _'timestamp'_ - an optional timestamp override (if not present, the server will use current time), expressed in milliseconds since 1970 (_new Date().getTime()_). | ||
- _'getLog(tags)'_ - Returns an array of events which match the tag(s) specifed. | ||
For example: | ||
```javascript | ||
var Hapi = require('hapi'); | ||
// Create Hapi servers | ||
var http = new Hapi.Server('0.0.0.0', 8080); | ||
// Route handler | ||
var testLogs = function (request) { | ||
request.log('error', new Error('Something failed')); | ||
if (request.getLog('error').length === 0) { | ||
request.reply('Success!'); | ||
} | ||
else { | ||
request.reply('Failure!'); | ||
} | ||
}; | ||
// Set routes | ||
http.addRoute({ method: 'GET', path: '/', handler: testLogs }); | ||
// Start Hapi servers | ||
http.start(); | ||
``` | ||
The 'request.log' method is always available. | ||
@@ -577,5 +619,7 @@ | ||
**hapi** provides a simple facility for keeping track of pending tails by providing the following request methods: | ||
- _'addTail([name])'_ - registers a named tail and returns a tail id. The tail id must be retained and used to remove the tail when completed. The method is available on every event or extension hook prior to the 'tail' event. | ||
- _'removeTail(tailId)'_ - removes a tail to notify the server that the associated action has been completed. | ||
- _'addTail([name])'_ - registers a named tail and returns a tail function. The tail function must be retained and used to remove the tail when completed. The method is available on every event or extension hook prior to the 'tail' event. | ||
- _'removeTail(tail)'_ - removes a tail to notify the server that the associated action has been completed. | ||
Alternatively, the returned tail function can be called directly without using the _removeTail()_ method. | ||
For example: | ||
@@ -591,12 +635,12 @@ ```javascript | ||
var tailId1 = request.addTail('tail1'); | ||
var tail1 = request.addTail('tail1'); | ||
setTimeout(function () { | ||
request.removeTail(tailId1); | ||
request.removeTail(tail1); // Using removeTail() interface | ||
}, 5000); | ||
var tailId2 = request.addTail('tail2'); | ||
var tail2 = request.addTail('tail2'); | ||
setTimeout(function () { | ||
request.removeTail(tailId2); | ||
tail2(); // Using tail function interface | ||
}, 2000); | ||
@@ -603,0 +647,0 @@ |
// Load modules | ||
var expect = require('chai').expect; | ||
var Hapi = require('../../lib/hapi'); | ||
var Hapi = process.env.TEST_COV ? require('../../lib-cov/hapi') : require('../../lib/hapi'); | ||
var S = Hapi.Types.String; | ||
describe('Documentation', function() { | ||
var _routeTemplate = '{{#each routes}}{{this.method}}|{{/each}}'; | ||
var _indexTemplate = '{{#each routes}}{{this.path}}|{{/each}}'; | ||
var _server = null; | ||
var _serverUrl = 'http://127.0.0.1:8083'; | ||
var _template = '{{#each routes}}{{this.method}}|{{/each}}'; | ||
var _http = new Hapi.Server('0.0.0.0', 8083, { name: 'test', docs: { template: _template }}); | ||
var _serverUrl = 'http://127.0.0.1:8083'; | ||
function setupServer(done) { | ||
var handler = function(request) { | ||
@@ -17,24 +17,32 @@ request.reply('ok'); | ||
_http.addRoutes([ | ||
{ method: 'GET', path: '/test', config: { handler: handler, query: { param1: S() } } }, | ||
{ method: 'POST', path: '/test', config: { handler: handler, query: { param2: S() } } } | ||
]); | ||
function setupServer(done) { | ||
_server = new Hapi.Server('0.0.0.0', 8083, { authentication: false, docs: { routeTemplate: _routeTemplate, indexTemplate: _indexTemplate }}); | ||
_server.addRoutes([ | ||
{ method: 'GET', path: '/test', config: { handler: handler, query: { param1: S().required() } } }, | ||
{ method: 'POST', path: '/test', config: { handler: handler, query: { param2: S().valid('first', 'last') } } } | ||
]); | ||
_server.listener.on('listening', function() { | ||
done(); | ||
}); | ||
_server.start(); | ||
} | ||
_http.start(); | ||
done(); | ||
} | ||
function teardownServer(done) { | ||
_server.stop(); | ||
done(); | ||
} | ||
function makeRequest(path, callback) { | ||
var next = function(res) { | ||
return callback(res.result); | ||
}; | ||
function makeRequest(path, callback) { | ||
var next = function(res) { | ||
return callback(res.result); | ||
}; | ||
_http.inject({ | ||
method: 'get', | ||
url: _serverUrl + path | ||
}, next); | ||
} | ||
_server.inject({ | ||
method: 'get', | ||
url: _serverUrl + path | ||
}, next); | ||
} | ||
describe('Documentation generator', function() { | ||
before(setupServer); | ||
after(teardownServer); | ||
@@ -48,5 +56,5 @@ it('shows template when correct path is provided', function(done) { | ||
it('has a null response when wrong path is provided', function(done) { | ||
it('has a Not Found response when wrong path is provided', function(done) { | ||
makeRequest('/docs?path=blah', function(res) { | ||
expect(res).to.be.null; | ||
expect(res.error).to.equal('Not Found'); | ||
done(); | ||
@@ -56,8 +64,15 @@ }); | ||
it('has an error response if no path is provided', function(done) { | ||
it('displays the index if no path is provided', function(done) { | ||
makeRequest('/docs', function(res) { | ||
expect(res.error).to.exist; | ||
expect(res).to.equal('/test|/test|'); | ||
done(); | ||
}); | ||
}); | ||
it('the index does\'t have the docs endpoint listed', function(done) { | ||
makeRequest('/docs', function(res) { | ||
expect(res).to.not.contain('/docs'); | ||
done(); | ||
}); | ||
}); | ||
}); |
var should = require("should"); | ||
var qs = require("querystring"); | ||
var Validation = require("../../lib/validation"); | ||
var Validation = process.env.TEST_COV ? require('../../lib-cov/validation') : require('../../lib/validation'); | ||
var Types = require('joi').Types; | ||
@@ -6,0 +6,0 @@ var S = Types.String, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
354293
67
5001
707
23