summaryrefslogtreecommitdiff
path: root/app/node_modules
diff options
context:
space:
mode:
Diffstat (limited to 'app/node_modules')
-rw-r--r--app/node_modules/okadminview/index.js320
-rw-r--r--app/node_modules/okadminview/package.json17
-rw-r--r--app/node_modules/okdb/index.js9
-rw-r--r--app/node_modules/okquery/index.js26
-rw-r--r--app/node_modules/okresource/index.js209
-rw-r--r--app/node_modules/okresource/package.json1
-rw-r--r--app/node_modules/okrest/index.js20
-rw-r--r--app/node_modules/okrest/package.json11
-rw-r--r--app/node_modules/okserver/index.js7
-rw-r--r--app/node_modules/oktemplate/index.js7
-rw-r--r--app/node_modules/oktemplate/package.json3
-rw-r--r--app/node_modules/okutil/index.js29
-rw-r--r--app/node_modules/okview/index.js90
-rw-r--r--app/node_modules/okview/package.json7
14 files changed, 640 insertions, 116 deletions
diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js
new file mode 100644
index 0000000..42e7f36
--- /dev/null
+++ b/app/node_modules/okadminview/index.js
@@ -0,0 +1,320 @@
+var bodyParser = require('body-parser');
+var methodOverride = require('method-override');
+var Q = require('q');
+var pluralize = require('pluralize');
+var OKQuery = require('okquery');
+
+/**
+ * OKAdminView!
+ */
+function OKAdminView(options) {
+ if (!(this instanceof OKAdminView)) return new OKAdminView(options);
+ if (!options.app)
+ throw new Error('No Express app provided to OKAdminView');
+ if (!options.express)
+ throw new Error('No Express provided to OKAdminView');
+ if (!options.resourceConfig)
+ throw new Error('No resourceConfig provided to OKAdminView');
+ if (!options.resourceCache)
+ throw new Error('No resourceCache provided to OKAdminView');
+ if (!options.templateProvider)
+ throw new Error('No templateProvider provided to OKAdminView');
+ if (!options.meta)
+ throw new Error('No meta query provided to OKAdminView');
+ var app = options.app;
+ var express = options.express;
+ var meta = options.meta;
+ var resourceCache = this._resourceCache = options.resourceCache;
+ var resourceConfig = this._resourceConfig = options.resourceConfig;
+ var provider = options.templateProvider;
+ // Load templates
+ var templates = this._templates =
+ ['index', 'resource', 'resource_new'].reduce(function(cache, name) {
+ var template = provider.getTemplate(name);
+ if (!template)
+ throw new Error('Admin theme does not have needed template: "' + name + '"');
+ cache[name] = template;
+ return cache;
+ }, {});
+ // OKAdmin middleware is a router, so mounts on 'use'
+ Object.defineProperty(this, 'mount', {
+ value: 'use',
+ writable: false,
+ enumerable: true
+ });
+
+ // Resources which apper on the index are a function of
+ // the resource configuration.
+ var indexQueries = this._indexQueries = Object.keys(resourceConfig)
+ .map(function(key) {
+ var config = resourceConfig[key];
+ var type = config.type;
+ var staticData = config.static || {};
+ var resource = resourceCache.get(config.type);
+ if (!resource)
+ throw new Error('Something weird is going on');
+ var id = staticData[resource.idField];
+ resource = resourceCache.get(type, id) || resource;
+ if (resource.bound) {
+ // Resource instances implement the query API
+ return OKQuery({resource: resource});;
+ } else {
+ return OKQuery({resource: resource, query: config.query})
+ }
+ });
+
+ this._middleware = createMiddleware(this);
+
+ function createMiddleware(view) {
+ var router = express.Router({
+ strict: app.get('strict routing')
+ });
+
+ // Parse form data
+ router.use(bodyParser.urlencoded({extended: true}));
+ // HTML forms only support POST and GET methods
+ // We extend this by adding hidden input fields to the forms which
+ // specify the actual method desired.
+ router.use(methodOverride(function(req, res) {
+ // Parse out the hidden field
+ if (req.body && typeof req.body === 'object' && '_method' in req.body) {
+ var method = req.body._method;
+ delete req.body._method
+ return method
+ }
+ }));
+
+ router.get('/', function readIndex(req, res, next) {
+ fetchIndexTemplateData(meta, indexQueries).then(function(data) {
+ view.renderIndex(req, res, data);
+ }).fail(errorHandler(req, res));
+ });
+
+ router.get('/:type/new/', function createResourceView(req, res, next) {
+ var type = req.params.type || '';
+ var resource = resourceCache.get(type);
+ if (!resource) {
+ errorHandler(req, res)(new Error('No such resource ' + type));
+ } else {
+ meta.get().then(function(metadata) {
+ view.renderResourceNew(req, res, {
+ meta: metadata,
+ resource: {
+ type: resource.type,
+ spec: resource.spec
+ }
+ });
+ }).fail(errorHandler(req, res));
+ }
+ });
+
+
+ router.get('/:type/:id/', function readResource(req, res, next) {
+ var type = req.params.type || '';
+ var id = req.params.id || '';
+ var resource = resourceCache.get(type, id);
+ if (!resource) {
+ errorHandler(req, res)(new Error('No such resource'));
+ } else {
+ var query = OKQuery({
+ resource: resource,
+ query: id
+ });
+ fetchResourceTemplateData(meta, query, transform).then(function(data) {
+ if (!data) {
+ resourceMissingHandler(req, res)()
+ } else {
+ view.renderResource(req, res, data);
+ }
+ }).fail(errorHandler(req, res));
+ }
+
+ function transform(meta, resource, data) {
+ meta = meta || {};
+ resource = resource || {};
+ data = data || {};
+ var spec = Object.keys(resource.spec).reduce(function(cache, prop) {
+ var propSpec = resource.spec[prop];
+ var value = data[prop];
+ cache[prop].id = data[resource.idField];
+ cache[prop].value = value;
+ return cache;
+ }, resource.spec);
+ return {
+ meta: meta,
+ resource: {
+ type: resource.type,
+ spec: spec
+ }
+ };
+ }
+ });
+
+ router.post('/:type/', function createResource(req, res, next) {
+ var type = req.params.type;
+ var resource = resourceCache.get(type);
+ var data = req.body;
+ if (!resource) {
+ errorHandler(req, res)(new Error('No such resource ' + type));
+ } else {
+ meta.get().then(function(metadata) {
+ var templateData = {
+ meta: metadata,
+ resource: {
+ type: resource.type,
+ spec: resource.spec,
+ data: data
+ }
+ };
+ try {
+ resource.assertValid(data);
+ resource.create(data).then(function(created) {
+ res.redirect(303, data[resource.idField]);
+ }).fail(errorHandler(req, res));
+ } catch (errors) {
+ view.renderResource(req, res, templateData);
+ }
+ }).fail(errorHandler(req, res));;
+ }
+ });
+
+ router.put('/:type/:id/', function updateResource(req, res, next) {
+ var type = req.params.type;
+ var id = req.params.id;
+ var data = req.body;
+ var resource = resourceCache.get(type, id);
+ if (!resource) {
+ errorHandler(req, res)(new Error('No such resource ' + type));
+ } else {
+ // TODO Maybe should make metadata synchronous...
+ meta.get().then(function(metadata) {
+ var templateData = {
+ meta: metadata,
+ resource: {
+ spec: resource.spec,
+ data: data
+ }
+ };
+ try {
+ resource.assertValid(data);
+ resource.update(id, data).then(function(updated) {
+ res.redirect(303, '../' + updated[resource.idField]);
+ }).fail(errorHandler(req, res));
+ } catch (errors) {
+ view.renderResource(req, res, templateData);
+ }
+ }).fail(errorHandler(req, res));
+ }
+ });
+
+ return router;
+ }
+}
+
+OKAdminView.prototype.middleware = function() {
+ return this._middleware;
+};
+
+OKAdminView.prototype.renderIndex = function(req, res, data) {
+ data = data || {};
+ this._templates['index'].render(data).then(function(rendered) {
+ res.send(rendered);
+ }).fail(errorHandler(req, res));
+};
+
+OKAdminView.prototype.renderResource = function(req, res, data) {
+ data = data || {};
+ this._templates['resource'].render(data).then(function(rendered) {
+ res.send(rendered);
+ }).fail(errorHandler(req, res));
+};
+
+OKAdminView.prototype.renderResourceNew = function(req, res, data) {
+ data = data || {meta: {}, resource: {}};
+ this._templates['resource_new'].render(data).then(function(rendered) {
+ res.send(rendered);
+ }).fail(errorHandler(req, res));
+};
+
+/**
+ * Annotate template data with schema info
+ */
+function fetchIndexTemplateData(meta, queries) {
+ return Q.Promise(function(resolve, reject) {
+ Q.all([meta.get()].concat(queries.map(function(query) {
+ return query.get();
+ }))).then(function(results) {
+ var meta = results.shift();
+ var resources = results.reduce(function(cache, result, i) {
+ if (!result)
+ return cache;
+ var resource = queries[i].resource;
+ // We want the raw object spec
+ var spec = resource.spec;
+ var key = pluralize(resource.type);
+ if (!cache[key]) {
+ cache[key] = {
+ type: resource.type,
+ spec: spec,
+ data: []
+ };
+ }
+
+ if (result.length) {
+ result.forEach(addToCache)
+ } else {
+ addToCache(result);
+ }
+
+ function addToCache(data) {
+ // Report id to template under standard name
+ data.id = data[resource.idField];
+ cache[key].data.push(data);
+ }
+
+ return cache;
+ }, {});
+
+ resolve({
+ meta: meta,
+ resources: resources
+ });
+ }).fail(reject);
+ });
+}
+
+/**
+ * Annotate template data with schema info
+ */
+function fetchResourceTemplateData(meta, query, fn) {
+ fn = fn || function(m, r, d) { return {meta: m, resource: d}; };
+ return Q.Promise(function(resolve, reject) {
+ meta.get().then(function(metadata) {
+ query.get().then(function(data) {
+ var resource = query.resource;
+ resolve(fn(metadata, resource, data));
+ }).fail(reject);
+ }).fail(reject)
+ });
+}
+
+/**
+ * TODO Real error handling
+ */
+function errorHandler(req, res) {
+ return function(err) {
+ res.send(err.stack);
+ };
+}
+
+/**
+ * TODO Real 404 handling
+ */
+function resourceMissingHandler(req, res) {
+ return function() {
+ res.status(404);
+ res.send('404');
+ }
+}
+
+module.exports = OKAdminView;
diff --git a/app/node_modules/okadminview/package.json b/app/node_modules/okadminview/package.json
new file mode 100644
index 0000000..b07cb1a
--- /dev/null
+++ b/app/node_modules/okadminview/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "okadminview",
+ "version": "1.0.0",
+ "description": "administrate!",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "OKFocus",
+ "license": "None",
+ "dependencies": {
+ "body-parser": "^1.12.2",
+ "method-override": "^2.3.2",
+ "pluralize": "^1.1.2",
+ "q": "^1.2.0"
+ }
+}
diff --git a/app/node_modules/okdb/index.js b/app/node_modules/okdb/index.js
index 3089411..5368e4a 100644
--- a/app/node_modules/okdb/index.js
+++ b/app/node_modules/okdb/index.js
@@ -72,8 +72,13 @@ FSDB.prototype.put = function(collection, query, data) {
}
};
-FSDB.prototype.create = function(collection, id, data) {
- throw new Error('Not implemented!');
+FSDB.prototype.create = function(collection, data) {
+ var created = this._db(collection)
+ .chain()
+ .push(data)
+ .last()
+ .value();
+ return this._resolve(created);
};
FSDB.prototype.remove = function(collection, id, data) {
diff --git a/app/node_modules/okquery/index.js b/app/node_modules/okquery/index.js
index a64f0f2..9748067 100644
--- a/app/node_modules/okquery/index.js
+++ b/app/node_modules/okquery/index.js
@@ -14,14 +14,19 @@ function OKQuery(options) {
var resource = options.resource;
var type = resource.type;
var query = options.query || '*';
+
Object.defineProperty(this, 'resource', {
value: resource,
- writable: false
+ writable: false,
+ enumerable: true
});
+
Object.defineProperty(this, 'type', {
value: resource.type,
- writable: false
+ writable: false,
+ enumerable: true
});
+
this.get = createQuery(resource, query, {
default: options.default
});
@@ -29,8 +34,9 @@ function OKQuery(options) {
function createQuery(resource, query, options) {
options = options || {};
- var query;
- if (isDynamic(query)) {
+ if (resource.bound) {
+ query = queryBound(resource);
+ } else if (isDynamic(query)) {
query = queryDynamic(resource);
} else if (isSet(query)) {
query = queryAll(resource);
@@ -46,19 +52,25 @@ function createQuery(resource, query, options) {
function queryDynamic(resource) {
return function(id) {
return resource.get(id);
- }
+ };
}
function queryAll(resource) {
return function() {
return resource.all();
- }
+ };
}
function querySingle(resource, id) {
return function() {
return resource.get(id);
- }
+ };
+}
+
+function queryBound(resource) {
+ return function() {
+ return resource.get();
+ };
}
function withDefault(queryFn, resultDefault) {
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;
diff --git a/app/node_modules/okresource/package.json b/app/node_modules/okresource/package.json
index da9dfe0..7f19c9b 100644
--- a/app/node_modules/okresource/package.json
+++ b/app/node_modules/okresource/package.json
@@ -9,6 +9,7 @@
"author": "OKFocus",
"license": "None",
"dependencies": {
+ "object-assign": "^2.0.0",
"q": "^1.2.0"
}
}
diff --git a/app/node_modules/okrest/index.js b/app/node_modules/okrest/index.js
deleted file mode 100644
index 169626d..0000000
--- a/app/node_modules/okrest/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var OKView = require('okview');
-
-/**
- * OKRestEndpoint!
- * Takes a resources and creates a CRUD endpoint.
- */
-function OKRestEndpoint(resource, options) {
- if (!(this instanceof OKRestEndpoint)) return new OKRestEndpoint(resource, options);
- options = options || {};
- this._resource = resource;
-}
-
-OKRestEndpoint.prototype.middleware = function() {
- var self = this;
- return function handleREST(req, res, next) {
- res.send(self._resource.name);
- };
-}
-
-module.exports = OKRestEndpoint;
diff --git a/app/node_modules/okrest/package.json b/app/node_modules/okrest/package.json
deleted file mode 100644
index 462c890..0000000
--- a/app/node_modules/okrest/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "okrest",
- "version": "1.0.0",
- "description": "nice",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "OKFocus",
- "license": "None"
-}
diff --git a/app/node_modules/okserver/index.js b/app/node_modules/okserver/index.js
index 952602d..e6145ee 100644
--- a/app/node_modules/okserver/index.js
+++ b/app/node_modules/okserver/index.js
@@ -45,9 +45,14 @@ function OKServer(options) {
* Order is important here! Requests go down the middleware
* chain until they are handled with a response, which could
* happen anywhere in the chain. Watch out for middleware shadowing
- * ither middleware.
+ * other middleware.
*/
+ // Intercept favicon requests and 404 for now
+ app.use('/favicon.ico', function(req, res) {
+ res.status(404)
+ return res.send('');
+ });
// Serve user static files
app.use(express.static(root));
// Serve admin interface static files
diff --git a/app/node_modules/oktemplate/index.js b/app/node_modules/oktemplate/index.js
index 700020c..dafe5e6 100644
--- a/app/node_modules/oktemplate/index.js
+++ b/app/node_modules/oktemplate/index.js
@@ -1,3 +1,4 @@
+var Q = require('q');
var fs = require('fs');
var path = require('path');
var glob = require('glob');
@@ -56,8 +57,12 @@ OKTemplateRepo.prototype._populateCache = function _populateCache(engine, cache,
name: name,
templateString: templateString,
render: function(data) {
+ return Q.promise(function(resolve, reject) {
// TODO Not sure if this caches parsed templates behind the scenes?
- return engine.parseAndRender(templateString, data);
+ engine.parseAndRender(templateString, data)
+ .then(resolve)
+ .catch(reject);
+ });
}
}
});
diff --git a/app/node_modules/oktemplate/package.json b/app/node_modules/oktemplate/package.json
index 3e92ccb..70e94e3 100644
--- a/app/node_modules/oktemplate/package.json
+++ b/app/node_modules/oktemplate/package.json
@@ -12,6 +12,7 @@
"bluebird": "^2.9.21",
"glob": "^5.0.3",
"json-to-html": "^0.1.2",
- "liquid-node": "^2.5.0"
+ "liquid-node": "^2.5.0",
+ "q": "^1.2.0"
}
}
diff --git a/app/node_modules/okutil/index.js b/app/node_modules/okutil/index.js
index 738c6a4..3142ae1 100644
--- a/app/node_modules/okutil/index.js
+++ b/app/node_modules/okutil/index.js
@@ -10,35 +10,6 @@ var Q = require('q');
module.exports = {
/**
- * Takes a meta data query and an array of resource queries
- * and returns a promise for an object merging all queried
- * data, pluralizing keys where necessary.
- *
- * Lil bit convoluted, sorry.
- */
- fetchTemplateData: function fetchTemplateData(meta, queries, options) {
- return Q.promise(function(resolve, reject) {
- return Q.all(
- [meta.get()].concat(queries.map(function(query) {
- return query.get(options);
- })))
- .then(function(results) {
- var metadata = results.shift();
- var normalized = results.reduce(function(data, result, i) {
- var type = queries[i].type;
- if (isarray(result)) {
- data[pluralize(type)] = result;
- } else {
- data[type] = result;
- }
- return data;
- }, {meta: metadata});
- resolve(normalized);
- }, reject);
- });
- },
-
- /**
* Return a copy of the route with a trailing slash
*/
withTrailingSlash: function withTrailingSlash(route) {
diff --git a/app/node_modules/okview/index.js b/app/node_modules/okview/index.js
index 1ceac03..c245b0c 100644
--- a/app/node_modules/okview/index.js
+++ b/app/node_modules/okview/index.js
@@ -1,4 +1,8 @@
-var fetchTemplateData = require('okutil').fetchTemplateData;
+var assign = require('object-assign');
+var pluralize = require('pluralize');
+var isarray = require('lodash.isarray');
+var Q = require('q');
+var OKQuery = require('okquery');
var OKResource = require('okresource');
// Routes for views over collections have a special pattern
@@ -15,11 +19,11 @@ function OKView(options) {
if (!(this instanceof OKView)) return new OKView(options);
options = options || {};
if (!options.template)
- throw new Error('No template provided to view.');
+ throw new Error('No template provided to OKView.');
if (!options.meta)
- throw new Error('No meta resource provided to view');
+ throw new Error('No meta resource provided to OKView');
if (!options.route)
- throw new Error('No route provided to view');
+ throw new Error('No route provided to OKView');
var route = options.route;
var mount = options.mount || 'get';
this._template = options.template;
@@ -29,28 +33,32 @@ function OKView(options) {
// resource will be resolved later
// TODO This bound / unbound thing can probably be expressed in a
// less convoluted way.
- var unbound = this.unbound = !!UNBOUND_ROUTE_PATTERN.exec(this.route);
+ var unbound = this.unbound = !!UNBOUND_ROUTE_PATTERN.exec(route);
+
Object.defineProperty(this, 'mount', {
value: mount,
writable: false,
enumerable: true
});
+
Object.defineProperty(this, 'route', {
value: route,
writable: false,
enumerable: true
});
+
this._middleware = createMiddleware(this);
- this._fetchTemplateData = unbound ?
- fetchResourceTemplateData : fetchCollectionTemplateData;
+ this._fetchTemplateData = unbound ? fetchUnbound : fetchBound;
- function fetchResourceTemplateData(id) {
- // Bound views only have a single query
- // TODO This is super convoluted
- return fetchTemplateData(meta, [queries[0].get(id)]);
+ function fetchUnbound(id) {
+ var resource = queries[0].resource;
+ return fetchTemplateData(meta, [OKQuery({
+ resource: resource,
+ query: id
+ })]);
}
- function fetchCollectionTemplateData() {
+ function fetchBound() {
return fetchTemplateData(meta, queries);
}
}
@@ -62,7 +70,7 @@ OKView.prototype.middleware = function() {
OKView.prototype.render = function(req, res, data) {
this._template.render(data).then(function(html) {
res.send(html);
- }, errorHandler(req, res, data));
+ }).fail(errorHandler(req, res, data));
};
OKView.prototype.fetchTemplateData = function() {
@@ -94,7 +102,7 @@ function unboundMiddleware(view) {
var id = req.params[paramName];
view.fetchTemplateData(id).then(function(data) {
view.render(req, res, data);
- }, errorHandler(req, res, next));
+ }).fail(errorHandler(req, res, next));
};
}
@@ -106,7 +114,7 @@ function boundMiddleware(view) {
return function(req, res, next) {
view.fetchTemplateData().then(function(data) {
view.render(req, res, data);
- }, errorHandler(req, res, next));
+ }).fail(errorHandler(req, res, next));
};
}
@@ -129,4 +137,56 @@ function getParamName(route) {
return matches[1];
}
+/**
+ * Takes a meta data query and an array of resource queries
+ * and returns a promise for an object merging all queried
+ * data, pluralizing keys where necessary.
+ *
+ * Lil bit convoluted, sorry.
+ */
+function fetchTemplateData(meta, queries) {
+ return Q.promise(function(resolve, reject) {
+ return Q.all(
+ [meta.get()].concat(queries.map(function(query) {
+ return query.get();
+ })))
+ .then(function(results) {
+ var metadata = results.shift();
+ var normalized = results.reduce(function(cache, result, i) {
+ // Could be just some rogue request
+ if (!result) {
+ return cache;
+ }
+ var resource = queries[i].resource;
+ var type = queries[i].type;
+ var manyResult = isarray(result);
+ // Inform template of ID in generic field
+ if (manyResult) {
+ result = result.map(function(data) {
+ return assign({}, data, {id: data[resource.idField]})
+ });
+ } else {
+ result = assign({}, result, {id: result[resource.idField]});
+ }
+ // If we have a lot of results for a certain type,
+ // we pluralize the key and yield an array of results
+ if (cache[type] || manyResult) {
+ var plural = pluralize(type);
+ delete cache[type];
+ cache[plural] = [];
+ if (manyResult) {
+ cache[plural] = cache[plural].concat(result);
+ } else {
+ cache[plural].push(result);
+ }
+ } else {
+ cache[type] = result;
+ }
+ return cache;
+ }, {meta: metadata});
+ resolve(normalized);
+ }).fail(reject);
+ });
+}
+
module.exports = OKView;
diff --git a/app/node_modules/okview/package.json b/app/node_modules/okview/package.json
index 8d95b40..bbf4e40 100644
--- a/app/node_modules/okview/package.json
+++ b/app/node_modules/okview/package.json
@@ -8,5 +8,10 @@
},
"author": "OKFocus",
"license": "None",
- "dependencies": {}
+ "dependencies": {
+ "lodash.isarray": "^3.0.1",
+ "object-assign": "^2.0.0",
+ "pluralize": "^1.1.2",
+ "q": "^1.2.0"
+ }
}