summaryrefslogtreecommitdiff
path: root/app/node_modules/okresource/index.js
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2015-04-08 19:56:21 -0400
committerJules Laplace <jules@okfoc.us>2015-04-08 19:56:21 -0400
commitfe1ea91f73059c146369ccb2404fcaf6d03d2f4d (patch)
tree9b8c5e37821e147048e5d1ddbe5435af47716949 /app/node_modules/okresource/index.js
parent0c6d7edc0042464c8f05d38e6933186719dd259e (diff)
parent32f87fcb58466e0e311349f96751c18e2a2cd7ea (diff)
k
Diffstat (limited to 'app/node_modules/okresource/index.js')
-rw-r--r--app/node_modules/okresource/index.js209
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;