diff options
Diffstat (limited to 'app/node_modules/okresource/index.js')
| -rw-r--r-- | app/node_modules/okresource/index.js | 204 |
1 files changed, 129 insertions, 75 deletions
diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index 94d8cfb..c1f2509 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -1,4 +1,6 @@ +var format = require('util').format var assign = require('object-assign'); +var cloneDeep = require('lodash.clonedeep'); var Q = require('q'); /** @@ -18,27 +20,44 @@ function OKResource(options) { 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.spec.id && 'id'); - if (!idField) - throw new Error('Bad schema: no ID field'); + var spec = schema.spec; var type = options.type; + var hasForeignKey = false this._db = options.db; this._schema = schema; + var foreignKeys = Object.keys(spec).reduce(function(acc, field) { + var fieldSpec = spec[field] + if (fieldSpec.type === 'foreign-key') { + hasForeignKey = true + acc[field] = fieldSpec.key + } + return acc + }, {}) + + // Will store references to other resources referenced via foreign keys + this._foreignKeyedResources = {} + // Define properties which are part of the API + + // Should be treated as read-only + Object.defineProperty(this, 'foreignKeys', { + get: function() { + return foreignKeys + } + }) + + Object.defineProperty(this, 'hasForeignKey', { + get: function() { + return hasForeignKey + } + }) Object.defineProperty(this, 'spec', { - value: schema.spec, - writable: false, + get: function() { + return schema.spec; + }, enumerable: true }); @@ -48,12 +67,6 @@ function OKResource(options) { enumerable: true }); - Object.defineProperty(this, 'idField', { - value: idField, - writable: false, - enumerable: true - }); - // Whether this resource represents a specific data point // or a whole class of data Object.defineProperty(this, 'bound', { @@ -63,6 +76,30 @@ function OKResource(options) { }); } +OKResource.prototype._linkForeignKey = function(field, resource) { + this._foreignKeyedResources[field] = resource +} + +/** + * Fetch all related resources for the given field + */ +OKResource.prototype.related = function(field) { + var resource = this._foreignKeyedResources[field] + return Q.promise(function(resolve, reject) { + if (!resource) { + return error(reject, new Error(format( + "No related resource for field '%s'", field))) + } + resource.all().then(resolve).fail(reject) + }) + + function error(reject, err) { + setTimeout(function() { + reject(err) + }, 0) + } +} + /** * Throws an error if data does not conform to schema */ @@ -71,31 +108,34 @@ OKResource.prototype.assertValid = function(data) { }; OKResource.prototype.all = function() { - return this._db.getAll(this.type); + return this._db.all(this.type); }; -OKResource.prototype.create = function(data) { +OKResource.prototype.getID = function(data) { data = data || {}; + return data[this._schema.idField]; +}; + +OKResource.prototype.create = function(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')); + if (!data) { + reject(new Error('No data provided')); } else { - db.create(type, data).then(resolve).fail(reject); + db.insert(type, data).then(resolve).fail(reject); } }); }; -OKResource.prototype.destroy = function(data) { - data = data || {}; - var id = data[this.idField]; +OKResource.prototype.destroy = function(id) { + var db = this._db; + var type = this.type; return Q.promise(function(resolve, reject) { if (!id) { - reject(new Error('Data does not contain ID property')); + reject(new Error('No ID provided')); } else { - this._db.remove(this.type, data.id, data).then(resolve).fail(reject); + db.remove(type, id).then(resolve).fail(reject); } }); }; @@ -106,7 +146,7 @@ OKResource.prototype.find = function(query) { var type = this.type; return Q.promise(function(resolve, reject) { if (!query) { - throw new Error('No query given'); + throw new Error('No query provided'); } else { db.find(type, query).then(resolve).fail(reject); } @@ -116,53 +156,65 @@ OKResource.prototype.find = function(query) { 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'); + throw new Error('No ID provided'); } 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).fail(reject); + db.get(type, id).then(resolve).fail(reject); } }); }; OKResource.prototype.update = function(id, data) { - data = data || {}; var db = this._db; var type = this.type; - var idField = this.idField; return Q.promise(function(resolve, reject) { if (!id) { reject(new Error('No resource ID provided')); + } else if (!data) { + reject(new Error('No data provided')); } else { - var query = {}; - query[idField] = id; - db.put(type, query, data).then(resolve).fail(reject);; + db.update(type, id, data).then(resolve).fail(reject);; } }); }; +OKResource.prototype.updateBatch = function(ids, datas) { + var self = this; + var db = this._db; + var type = this.type; + return Q.promise(function(resolve, reject) { + if (!ids || !ids.length || !datas || !datas.length || + ids.length !== datas.length) { + reject(new Error('Bad input')); + } else { + db.updateBatch(type, ids, datas).then(resolve).fail(reject); + } + }); +} + + +/** + * Get all documents in collection sorted by property, + * optionally in descending order + */ +OKResource.prototype.sortBy = function(prop, descend) { + return this._db.sortBy(this.type, prop, descend); +}; + OKResource.prototype.updateOrCreate = function(id, data) { data = data || {}; 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('No resource ID provided')); + reject(new Error('No ID provided')); } else { - db.get(type, query).then(function(persisted) { + db.get(type, id).then(function(persisted) { if (persisted) { - db.put(type, query, data).then(resolve).fail(reject); + db.update(type, id, data).then(resolve).fail(reject); } else { - db.create(type, data).then(resolve).fail(reject); + db.insert(type, data).then(resolve).fail(reject); } }).fail(reject); } @@ -179,6 +231,9 @@ OKResource.prototype.instance = function(options) { }); }; +/** + * TODO This class is such bullshit. Refactor out + */ function OKResourceInstance(resource, options) { if (!(this instanceof OKResourceInstance)) return new OKResourceInstance(options); // Only support static data instances for now @@ -190,12 +245,16 @@ function OKResourceInstance(resource, options) { // 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]; + var staticData = cloneDeep(options.static); + var id = resource.getID(staticData); if (!id) throw new Error( 'Cannot create static OKResourceInstance without an ID field'); + this.getID = function() { + return id; + }; + /** * Ensure that static data is provided on get */ @@ -204,9 +263,9 @@ function OKResourceInstance(resource, options) { resource.get(id).then(function(data) { // Note the assign call. Don't expose private references! if (data) { - resolve(assign({}, data, staticData)); + resolve(assign(data, cloneDeep(staticData))); } else { - resolve(assign({}, staticData)); + resolve(cloneDeep(staticData)); } }).fail(reject); }); @@ -274,6 +333,18 @@ function OKResourceInstance(resource, options) { resource.assertValid(data); }; + Object.defineProperty(this, 'foreignKeys', { + get: function() { + return [] + } + }) + + Object.defineProperty(this, 'hasForeignKey', { + get: function() { + return false + } + }) + Object.defineProperty(this, 'parent', { value: resource, writable: false, @@ -281,14 +352,9 @@ function OKResourceInstance(resource, options) { }); Object.defineProperty(this, 'spec', { - value: resource.spec, - writable: false, - enumerable: true - }); - - Object.defineProperty(this, 'id', { - value: id, - writable: false, + get: function() { + return resource.spec + }, enumerable: true }); @@ -298,23 +364,11 @@ function OKResourceInstance(resource, options) { 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; |
