var Q = require('q'); /** * OKResource! * TODO Would be nicer to compose this with an existing resource * module, but haven't found a good fit. Should keep an eye on * 'resource' by bigcompany for an update. */ function OKResource(options) { if (!(this instanceof OKResource)) return new OKResource(options); options = options || {}; if (!options.type) throw new Error('No resource type provided to OKResource') if (!options.schema) throw new Error('No schema provided to OKResource'); if (!options.db) throw new Error('No DB provided to OKResource'); var schema = options.schema; // Iterate through spec to find field which will act as the // resource id in da db. var idField = Object.keys(schema.spec).reduce(function(idField, prop) { var spec = schema.spec[prop]; if (spec.id) idField = prop; return idField; // If schema has a prop called 'id', default to that one }, schema.id && 'id'); if (!idField) throw new Error('Bad schema: no ID field'); var type = options.type; this._db = options.db; // 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, enumerable: true }); Object.defineProperty(this, 'type', { value: type, writable: false, enumerable: true }); Object.defineProperty(this, 'idField', { value: idField, writable: false, enumerable: true }); } /** * Throws an error if data does not conform to schema */ OKResource.prototype.assertValid = function(data) { this.schema.assertValid(data); }; OKResource.prototype.all = function() { return this._db.getAll(this.type); }; OKResource.prototype.create = function(data) { data = data || {}; 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); } }); }; OKResource.prototype.destroy = function(data) { data = data || {}; 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.remove(this.type, data.id, data).then(resolve, reject); } }); }; OKResource.prototype.find = function(query) { return Q.promise(function(resolve, reject) { if (!query) { throw new Error('No query given'); } else { this._db.find(this.type, query).then(resolve, reject); } }); }; OKResource.prototype.get = function(id) { var db = this._db; var type = this.type; var idField = this.idField; return Q.promise(function(resolve, reject) { if (!id) { throw new Error('No ID given'); } else { // We have the id, but we still need // to resolve which field is the id field // to match var query = {}; query[idField] = id; db.get(type, query).then(resolve, reject); } }); }; OKResource.prototype.update = function(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')); } else { var query = {}; query[idField] = data[idField]; db.put(type, query, data).then(resolve, reject);; } }); }; OKResource.prototype.updateOrCreate = function(data) { data = data || {}; var id = data[this.idField]; var type = this.type; var db = this._db; var idField = this.idField; return Q.promise(function(resolve, reject) { if (!id) { reject(new Error('Cannot updateOrCreate without ID')); } 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); } }); }; module.exports = OKResource;