diff options
| author | Jules Laplace <jules@okfoc.us> | 2015-04-08 19:56:21 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2015-04-08 19:56:21 -0400 |
| commit | fe1ea91f73059c146369ccb2404fcaf6d03d2f4d (patch) | |
| tree | 9b8c5e37821e147048e5d1ddbe5435af47716949 /app/node_modules/okresource/index.js | |
| parent | 0c6d7edc0042464c8f05d38e6933186719dd259e (diff) | |
| parent | 32f87fcb58466e0e311349f96751c18e2a2cd7ea (diff) | |
k
Diffstat (limited to 'app/node_modules/okresource/index.js')
| -rw-r--r-- | app/node_modules/okresource/index.js | 209 |
1 files changed, 181 insertions, 28 deletions
diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index 7cd51c7..80279dc 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -1,3 +1,4 @@ +var assign = require('object-assign'); var Q = require('q'); /** @@ -25,21 +26,16 @@ function OKResource(options) { idField = prop; return idField; // If schema has a prop called 'id', default to that one - }, schema.id && 'id'); + }, schema.spec.id && 'id'); if (!idField) throw new Error('Bad schema: no ID field'); var type = options.type; this._db = options.db; + this._schema = schema; // Define properties which are part of the API - Object.defineProperty(this, 'schema', { - value: schema, - writable: false, - enumerable: true - }); - Object.defineProperty(this, 'spec', { value: schema.spec, writable: false, @@ -57,13 +53,21 @@ function OKResource(options) { writable: false, enumerable: true }); + + // Whether this resource represents a specific data point + // or a whole class of data + Object.defineProperty(this, 'bound', { + value: false, + writable: false, + enumerable: true + }); } /** * Throws an error if data does not conform to schema */ OKResource.prototype.assertValid = function(data) { - this.schema.assertValid(data); + this._schema.assertValid(data); }; OKResource.prototype.all = function() { @@ -72,12 +76,14 @@ OKResource.prototype.all = function() { OKResource.prototype.create = function(data) { data = data || {}; + var type = this.type; + var db = this._db; var id = data[this.idField]; return Q.promise(function(resolve, reject) { if (!id) { reject(new Error('Data does not contain ID property')); } else { - this._db.create(this.type, data).then(resolve, reject); + db.create(type, data).then(resolve).fail(reject); } }); }; @@ -89,7 +95,7 @@ OKResource.prototype.destroy = function(data) { if (!id) { reject(new Error('Data does not contain ID property')); } else { - this._db.remove(this.type, data.id, data).then(resolve, reject); + this._db.remove(this.type, data.id, data).then(resolve).fail(reject); } }); }; @@ -99,7 +105,7 @@ OKResource.prototype.find = function(query) { if (!query) { throw new Error('No query given'); } else { - this._db.find(this.type, query).then(resolve, reject); + this._db.find(this.type, query).then(resolve).fail(reject); } }); }; @@ -117,48 +123,195 @@ OKResource.prototype.get = function(id) { // to match var query = {}; query[idField] = id; - db.get(type, query).then(resolve, reject); + db.get(type, query).then(resolve).fail(reject); } }); }; -OKResource.prototype.update = function(data) { +OKResource.prototype.update = function(id, data) { data = data || {}; - var id = data[this.idField]; var db = this._db; var type = this.type; var idField = this.idField; return Q.promise(function(resolve, reject) { if (!id) { - reject(new Error('Data does not contain ID property')); + reject(new Error('No resource ID provided')); } else { var query = {}; - query[idField] = data[idField]; - db.put(type, query, data).then(resolve, reject);; + query[idField] = id; + db.put(type, query, data).then(resolve).fail(reject);; } }); }; -OKResource.prototype.updateOrCreate = function(data) { +OKResource.prototype.updateOrCreate = function(id, data) { data = data || {}; - var id = data[this.idField]; var type = this.type; var db = this._db; var idField = this.idField; + var query = {}; + query[idField] = id; return Q.promise(function(resolve, reject) { if (!id) { - reject(new Error('Cannot updateOrCreate without ID')); + reject(new Error('No resource ID provided')); } else { - db.get(type, data.id).then(function(cached) { - var query = {}; - query[idField] = id; - if (cached) - db.put(type, query, data).then(resolve, reject); - else - db.create(type, data).then(resolve, reject); - }, reject); + db.get(type, query).then(function(persisted) { + if (persisted) { + db.put(type, query, data).then(resolve).fail(reject); + } else { + db.create(type, data).then(resolve).fail(reject); + } + }).fail(reject); } }); }; +/** + * Create special resource which is bound to particular data point + * and has custom validation and query properties. + */ +OKResource.prototype.instance = function(options) { + return new OKResourceInstance(this, { + static: options.static + }); +}; + +function OKResourceInstance(resource, options) { + if (!(this instanceof OKResourceInstance)) return new OKResourceInstance(options); + // Only support static data instances for now + if (!options.static) + throw new Error( + 'Cannot create OKResourceInstance without static data'); + + // Resources with static data are a special case. They exist + // conceptually at all times since they are derived from app + // configuration, but may not actually be present + // in the database and need custom logic to handle this. + var staticData = assign({}, options.static); + var id = staticData[resource.idField]; + if (!id) + throw new Error( + 'Cannot create static OKResourceInstance without an ID field'); + + /** + * Ensure that static data is provided on get + */ + this.get = function() { + return Q.promise(function(resolve, reject) { + resource.get(id).then(function(data) { + // Note the assign call. Don't expose private references! + if (data) { + resolve(assign({}, data, staticData)); + } else { + resolve(assign({}, staticData)); + } + }).fail(reject); + }); + }; + + this.update = function(data) { + return Q.promise(function(resolve, reject) { + var valid = Object.keys(staticData).every(function(prop) { + return staticData[prop] === data[prop]; + }); + if (!valid) { + reject(new Error('Cannot update resource\'s static data')); + } else { + // When updating static resources, we create them if + // they don't actually exist in the DB + resource.updateOrCreate(id, data).then(resolve).fail(reject); + } + }); + }; + + this.updateOrCreate = function(data) { + return Q.promise(function(resolve, reject) { + reject(new Error('Cannot updateOrCreate static resource')); + }); + }; + + this.destroy = function(id) { + return Q.promise(function(resolve, reject) { + reject(new Error('Cannot destroy static resource')); + }); + }; + + this.all = function(id) { + return Q.promise(function(resolve, reject) { + reject(new Error('Cannot get all for static resource')); + }); + }; + + this.create = function(id) { + return Q.promise(function(resolve, reject) { + reject(new Error('Cannot create static resource')); + }); + }; + + this.find = function(id) { + return Q.promise(function(resolve, reject) { + reject(new Error('Cannot perform find on static resource')); + }); + }; + + this.assertValid = function(data) { + data = data || {}; + Object.keys(staticData).forEach(function(prop) { + if (staticData[prop] !== data[prop]) { + // Validation error is in mschema error format + throw [{ + property: prop, + constraint: 'static', + expected: staticData[prop], + actual: data[prop], + message: 'Data does not match static data' + }]; + } + }); + resource.assertValid(data); + }; + + Object.defineProperty(this, 'parent', { + value: resource, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'spec', { + value: resource.spec, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'id', { + value: id, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'type', { + value: resource.type, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'idField', { + value: resource.idField, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'bound', { + value: true, + writable: false, + enumerable: true + }); + + Object.defineProperty(this, 'class', { + value: resource, + writable: false, + enumerable: true + }); +} + module.exports = OKResource; |
