confidence
Advanced tools
Comparing version 0.1.0 to 0.2.0
259
lib/index.js
// Load modules | ||
var Hapi = require('hapi'); | ||
var Store = require('./store'); | ||
var Id = require('./id'); | ||
@@ -15,259 +15,4 @@ | ||
exports.Store = Store; | ||
exports.id = Id; | ||
// Plugin registration | ||
exports.register = function (plugin, options, next) { | ||
// Process options | ||
var idGenerator = internals.idGenerator(); | ||
var defaults = { | ||
prefix: '', | ||
setId: idGenerator.set, | ||
getId: idGenerator.get | ||
}; | ||
var settings = Hapi.utils.applyToDefaults(defaults, options || {}); | ||
Hapi.utils.assert(!settings.prefix || settings.prefix[0] === '/', 'Path prefix must begin with /'); | ||
// Create configuration store | ||
settings.store = new Store(); | ||
if (settings.tree) { | ||
var err = settings.store.load(settings.tree); | ||
if (err) { | ||
return next(err); | ||
} | ||
} | ||
// Client identifier | ||
var id = { | ||
method: 'POST', | ||
path: settings.prefix + '/id', | ||
config: { | ||
handler: internals.idHandler(settings), | ||
validate: { | ||
payload: { | ||
criteria: Hapi.types.Object().required() | ||
.description('Key-value pairs used for applying configuration filters'), | ||
} | ||
}, | ||
description: 'Issue a unique client identifier for given client profile' | ||
} | ||
}; | ||
// Filtered key value | ||
var key = { | ||
method: 'GET', | ||
path: settings.prefix + '/key/{id}/{key*}', | ||
config: { | ||
handler: internals.keyHandler(settings), | ||
validate: { | ||
path: { | ||
id: Hapi.types.String() | ||
.min(1).max(30).alphanum() | ||
.description('Configuration client identifier issued by the confidence API'), | ||
key: Hapi.types.String() | ||
.description('Full path to the requested key') | ||
}, | ||
query: { | ||
depth: Hapi.types.Number().min(1) | ||
.description('Number of nested keys to retrieve') | ||
} | ||
}, | ||
description: 'Retrieve a key value or a key tree for given client identifier' | ||
} | ||
}; | ||
// Combo client identifier + filtered key value | ||
var combo = { | ||
method: 'POST', | ||
path: settings.prefix + '/combo/{key*}', | ||
config: { | ||
pre: [ | ||
{ assign: 'id', method: internals.idHandler(settings), type: 'handler' } | ||
], | ||
handler: internals.comboHandler(settings), | ||
validate: { | ||
path: { | ||
key: Hapi.types.String() | ||
.description('Full path to the requested key') | ||
}, | ||
query: { | ||
depth: Hapi.types.Number().min(1) | ||
.description('Number of nested keys to retrieve') | ||
}, | ||
payload: { | ||
criteria: Hapi.types.Object().required() | ||
.description('Key-value pairs used for applying configuration filters'), | ||
} | ||
}, | ||
description: 'Obtain a client id and retrieve a key value in one request' | ||
} | ||
}; | ||
// Read raw key value | ||
var read = { | ||
method: 'GET', | ||
path: settings.prefix + '/node/{key*}', | ||
config: { | ||
handler: internals.readHandler(settings), | ||
validate: { | ||
path: { | ||
key: Hapi.types.String() | ||
.description('Full path to the requested key') | ||
}, | ||
query: { | ||
} | ||
}, | ||
description: 'Retrieve a raw key value' | ||
} | ||
}; | ||
// Write raw node | ||
var write = { | ||
method: 'POST', | ||
path: settings.prefix + '/node/{key*}', | ||
config: { | ||
handler: internals.writeHandler(settings), | ||
validate: { | ||
path: { | ||
key: Hapi.types.String() | ||
.description('Full path to the key being modified') | ||
}, | ||
payload: { | ||
} | ||
}, | ||
description: 'Set a raw key value' | ||
} | ||
}; | ||
plugin.select('api').route([id, key, combo]); | ||
plugin.select('admin').route([read, write]); | ||
return next(); | ||
}; | ||
internals.idHandler = function (settings) { | ||
return function () { | ||
var self = this; | ||
// Validate criteria | ||
var criteria = this.payload.criteria; | ||
var keys = Object.keys(criteria); | ||
for (var i = 0, il = keys.length; i < il; ++i) { | ||
var key = keys[i]; | ||
var value = criteria[key]; | ||
if (typeof value !== 'string') { | ||
return this.reply(Hapi.error.badRequest('Non-String criteria value for ' + key)); | ||
} | ||
if (!key.match(/^\w+$/)) { | ||
return this.reply(Hapi.error.badRequest('Invalid criteria key ' + key)); | ||
} | ||
if (!value.match(/^\w+$/)) { | ||
return this.reply(Hapi.error.badRequest('Invalid criteria value for ' + key)); | ||
} | ||
} | ||
// Generate id | ||
settings.setId(criteria, function (err, id) { | ||
self.reply(err || id); | ||
}); | ||
}; | ||
}; | ||
internals.keyHandler = function (settings) { | ||
return function () { | ||
var self = this; | ||
settings.getId(this.params.id, function (err, criteria) { | ||
if (err) { | ||
return self.reply(err); | ||
} | ||
settings.store.get('/' + self.params.key, criteria, self.query.depth, function (err, value) { | ||
if (err) { | ||
return self.reply(err); | ||
} | ||
self.reply(value !== null ? value : Hapi.error.notFound()); | ||
}); | ||
}); | ||
}; | ||
}; | ||
internals.comboHandler = function (settings) { | ||
return function () { | ||
var self = this; | ||
settings.store.get('/' + this.params.key, this.payload.criteria, this.query.depth, function (err, value) { | ||
self.reply({ id: self.pre.id, value: value || null }); // Ignores errors as response is based on id generation | ||
}); | ||
}; | ||
}; | ||
internals.readHandler = function (settings) { | ||
return function () { | ||
this.reply('ok'); | ||
}; | ||
}; | ||
internals.writeHandler = function (settings) { | ||
return function () { | ||
this.reply('ok'); | ||
}; | ||
}; | ||
internals.idGenerator = function () { | ||
var counter = 0; | ||
var cache = {}; | ||
return { | ||
set: function (criteria, callback) { | ||
cache[++counter] = criteria; | ||
return callback(null, counter); | ||
}, | ||
get: function (id, callback) { | ||
var criteria = cache[id]; | ||
if (!criteria) { | ||
return callback(Hapi.error.notFound('Unknown client id')); | ||
} | ||
return callback(null, criteria); | ||
} | ||
}; | ||
}; | ||
// Load modules | ||
var Hapi = require('hapi'); | ||
var Hoek = require('hoek'); | ||
var Boom = require('boom'); | ||
@@ -24,3 +25,3 @@ | ||
this._tree = Hapi.utils.clone(tree); | ||
this._tree = Hoek.clone(tree); | ||
return null; | ||
@@ -65,3 +66,3 @@ }; | ||
var e = Hapi.error.badRequest(reason); | ||
var e = Boom.badRequest(reason); | ||
e.path = path || '/'; | ||
@@ -144,2 +145,6 @@ return e; | ||
if (typeof range.limit !== 'number') { | ||
return error('Range limit must be a number'); | ||
} | ||
if (range.limit <= lastLimit) { | ||
@@ -227,3 +232,3 @@ return error('Range entries not sorted in ascending order - ' + range.limit + ' cannot come after ' + lastLimit); | ||
if (invalid) { | ||
return next(Hapi.error.badRequest('Bad key segment: ' + invalid)); | ||
return next(Boom.badRequest('Bad key segment: ' + invalid)); | ||
} | ||
@@ -230,0 +235,0 @@ } |
{ | ||
"name": "confidence", | ||
"description": "Configuration API", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)", | ||
@@ -18,8 +18,6 @@ "repository": "git://github.com/spumko/confidence", | ||
"dependencies": { | ||
"hoek": "1.x.x", | ||
"boom": "1.x.x" | ||
}, | ||
"peerDependencies": { | ||
"hapi": "1.x.x" | ||
}, | ||
"devDependencies": { | ||
"hapi": "1.x.x", | ||
"lab": "0.x.x" | ||
@@ -26,0 +24,0 @@ }, |
<a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a> | ||
![confidence Logo](https://raw.github.com/spumko/con/master/images/confidence.png) | ||
[**hapi**](https://github.com/spumko/hapi) Configuration API plugin | ||
Configuration format, API, and A/B testing | ||
[![Build Status](https://secure.travis-ci.org/spumko/confidence.png)](http://travis-ci.org/spumko/confidence) | ||
// Load modules | ||
var Lab = require('lab'); | ||
var Hapi = require('hapi'); | ||
var Confidence = require('../'); | ||
@@ -248,2 +247,10 @@ | ||
it('fails on range array element with non-number limit', function (done) { | ||
var err = Confidence.Store.validate({ key: { $filter: 'a', $range: [{ limit: 'a' }], $default: 1 } }); | ||
expect(err.message).to.equal('Range limit must be a number'); | ||
expect(err.path).to.equal('/key'); | ||
done(); | ||
}); | ||
it('fails on out of order range array elements', function (done) { | ||
@@ -250,0 +257,0 @@ |
1
13
65132
2
611
+ Addedboom@1.x.x
+ Addedhoek@1.x.x
- Removedalce@1.0.0(transitive)
- Removedasync@0.2.10(transitive)
- Removedcatbox@1.2.0(transitive)
- Removedconfidence@0.8.1(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removedcryptiles@1.0.1(transitive)
- Removedesprima@1.0.4(transitive)
- Removedestraverse@1.3.2(transitive)
- Removedhapi@1.20.0(transitive)
- Removedhawk@1.1.2(transitive)
- Removedhoek@2.16.3(transitive)
- Removedinherits@2.0.4(transitive)
- Removediron@1.0.0(transitive)
- Removedisarray@0.0.1(transitive)
- Removedjoi@2.9.0(transitive)
- Removedjoi-v1@1.2.5(transitive)
- Removedlru-cache@2.3.1(transitive)
- Removedmime@1.2.11(transitive)
- Removedminimist@0.0.10(transitive)
- Removedmultiparty@3.0.0(transitive)
- Removednegotiator@0.2.8(transitive)
- Removedoptimist@0.6.1(transitive)
- Removedreadable-stream@1.1.14(transitive)
- Removedsemver@2.0.11(transitive)
- Removedshot@1.7.0(transitive)
- Removedsntp@1.0.9(transitive)
- Removedstream-counter@0.2.0(transitive)
- Removedstring_decoder@0.10.31(transitive)
- Removedwordwrap@0.0.3(transitive)