From 70591083a1f7ea9ecf511b62d6a0a010a8da8c24 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 13:04:00 -0400 Subject: Stray console.log --- app/node_modules/okquery/index.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okquery/index.js b/app/node_modules/okquery/index.js index 33a49c4..2d93e2a 100644 --- a/app/node_modules/okquery/index.js +++ b/app/node_modules/okquery/index.js @@ -67,7 +67,6 @@ function queryComplex(resource, query) { if (notDynamic) { return function() { - console.log('get it!', query) return resource.find(query); } } else { @@ -75,7 +74,6 @@ function queryComplex(resource, query) { var dynamicQuery = {}; dynamicQuery[dynamicProp] = id; var query = assign({}, query, dynamicQuery); - console.log('get it!', query) return resource.find(query); } } -- cgit v1.2.3-70-g09d2 From ce294f2591279e1fc342b6c3da4a2e0c22c805a2 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 14:59:30 -0400 Subject: Don't expose object refs in public APIs ya dummy! Make sure to deep clone them input/output objects to maintain immutability y'hear --- app/node_modules/okresource/index.js | 21 ++++++++++++--------- app/node_modules/okresource/package.json | 1 + app/node_modules/okschema/index.js | 10 ++++++---- app/node_modules/okschema/package.json | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index 94d8cfb..5a643de 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -1,4 +1,5 @@ var assign = require('object-assign'); +var cloneDeep = require('cloneDeep'); var Q = require('q'); /** @@ -18,15 +19,16 @@ function OKResource(options) { throw new Error('No DB provided to OKResource'); var schema = options.schema; + var spec = schema.spec; // 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) + var idField = Object.keys(spec).reduce(function(idField, prop) { + var propSpec = spec[prop]; + if (propSpec.id) idField = prop; return idField; // If schema has a prop called 'id', default to that one - }, schema.spec.id && 'id'); + }, spec.id && 'id'); if (!idField) throw new Error('Bad schema: no ID field'); @@ -37,8 +39,9 @@ function OKResource(options) { // Define properties which are part of the API Object.defineProperty(this, 'spec', { - value: schema.spec, - writable: false, + get: function() { + return schema.spec; + }, enumerable: true }); @@ -190,7 +193,7 @@ 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 staticData = cloneDeep(options.static); var id = staticData[resource.idField]; if (!id) throw new Error( @@ -204,9 +207,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(assign({}, cloneDeep(staticData))); } }).fail(reject); }); diff --git a/app/node_modules/okresource/package.json b/app/node_modules/okresource/package.json index 7f19c9b..7b1dfbb 100644 --- a/app/node_modules/okresource/package.json +++ b/app/node_modules/okresource/package.json @@ -9,6 +9,7 @@ "author": "OKFocus", "license": "None", "dependencies": { + "lodash.clonedeep": "^3.0.0", "object-assign": "^2.0.0", "q": "^1.2.0" } diff --git a/app/node_modules/okschema/index.js b/app/node_modules/okschema/index.js index 8871a99..4b215d1 100644 --- a/app/node_modules/okschema/index.js +++ b/app/node_modules/okschema/index.js @@ -1,4 +1,4 @@ -var assign = require('object-assign'); +var cloneDeep = require('lodash.clonedeep'); var mschema = require('mschema'); var v = require('validator'); @@ -68,7 +68,7 @@ function OKSchema(spec) { if (!(this instanceof OKSchema)) return new OKSchema(spec); if (!spec) throw new Error('No spec provided to OKSchema'); - spec = assign({}, spec); + spec = cloneDeep(spec); // Cache the mschema version of our spec this._mschemaSpec = Object.keys(spec).reduce(function(cache, prop) { // If custom type, return its parent spec @@ -83,8 +83,10 @@ function OKSchema(spec) { }, {}); Object.defineProperty(this, 'spec', { - value: spec, - writable: false + get: function() { + return cloneDeep(spec); + }, + enumerable: true }); } diff --git a/app/node_modules/okschema/package.json b/app/node_modules/okschema/package.json index 21214fa..21a7c67 100644 --- a/app/node_modules/okschema/package.json +++ b/app/node_modules/okschema/package.json @@ -9,8 +9,8 @@ "author": "OKFocus", "license": "None", "dependencies": { + "lodash.clonedeep": "^3.0.0", "mschema": "^0.5.5", - "object-assign": "^2.0.0", "validator": "^3.37.0" } } -- cgit v1.2.3-70-g09d2 From d51924d7296af861ee2d9d95b8ded12708679a66 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 15:02:48 -0400 Subject: Oops. Resolve lodash.clonedeep correctly --- app/node_modules/okresource/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index 5a643de..febd1d5 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -1,5 +1,5 @@ var assign = require('object-assign'); -var cloneDeep = require('cloneDeep'); +var cloneDeep = require('lodash.clonedeep'); var Q = require('q'); /** -- cgit v1.2.3-70-g09d2 From 757f6992aea253b06cc4f65fa0dd7da71cf82135 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 15:59:19 -0400 Subject: Implement FSDB.remove --- app/node_modules/okdb/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okdb/index.js b/app/node_modules/okdb/index.js index 79ce1eb..4be646d 100644 --- a/app/node_modules/okdb/index.js +++ b/app/node_modules/okdb/index.js @@ -81,8 +81,15 @@ FSDB.prototype.create = function(collection, data) { return this._resolve(created); }; -FSDB.prototype.remove = function(collection, id, data) { - throw new Error('Not implemented!'); +FSDB.prototype.remove = function(collection, query) { + if (!collection || !query) { + return this._resolve(null, new Error('Bad input')); + } else { + var data = this._db(collection).removeWhere(query); + if (!data) + var error = new Error('Cannot remove nonexistent entry'); + return this._resolve(data, error); + } }; FSDB.prototype.find = function(collection, query) { -- cgit v1.2.3-70-g09d2 From c4d8ee7c431b3511bf26da68e952808b51d663c7 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 16:00:21 -0400 Subject: Add resource delete functionality to admin --- app/node_modules/okadminview/index.js | 15 +++++++++++++++ app/node_modules/okresource/index.js | 12 +++++++----- themes/okadmin/templates/resource.liquid | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index 987fe51..a376df5 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -175,6 +175,21 @@ function OKAdminView(options) { } }); + router.delete('/:type/:id/', function deleteResource(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 ' + type)); + } else { + meta.get().then(function(metadata) { + resource.destroy(id).then(function() { + res.redirect(303, '../..'); + }).fail(errorHandler(req, res)); + }).fail(errorHandler(req, res)); + } + }); + return router; } } diff --git a/app/node_modules/okresource/index.js b/app/node_modules/okresource/index.js index febd1d5..0e8498f 100644 --- a/app/node_modules/okresource/index.js +++ b/app/node_modules/okresource/index.js @@ -91,14 +91,16 @@ OKResource.prototype.create = function(data) { }); }; -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; + var query = {}; + query[this.idField] = id; return Q.promise(function(resolve, reject) { if (!id) { - reject(new Error('Data does not contain ID property')); + reject(new Error('No ID given')); } else { - this._db.remove(this.type, data.id, data).then(resolve).fail(reject); + db.remove(type, query).then(resolve).fail(reject); } }); }; diff --git a/themes/okadmin/templates/resource.liquid b/themes/okadmin/templates/resource.liquid index c0d348d..48e3ef2 100644 --- a/themes/okadmin/templates/resource.liquid +++ b/themes/okadmin/templates/resource.liquid @@ -14,6 +14,10 @@
+
+ + +
{% include 'partials/tail' %} -- cgit v1.2.3-70-g09d2 From e01c2a20352d252e40ea133d0f4665b2e7513582 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 18:48:10 -0400 Subject: Lil fixes + defensive programming --- app/node_modules/okadminview/index.js | 28 +++++++++++++++++----------- app/node_modules/okadminview/package.json | 1 + app/node_modules/okquery/index.js | 9 +++++++++ app/node_modules/okquery/package.json | 1 + app/node_modules/okserver/index.js | 10 ---------- 5 files changed, 28 insertions(+), 21 deletions(-) (limited to 'app/node_modules') diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index a376df5..05e2251 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -1,4 +1,5 @@ -var assign = require('object-assign') +var assign = require('object-assign'); +var cloneDeep = require('lodash.clonedeep'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); var Q = require('q'); @@ -22,12 +23,14 @@ function OKAdminView(options) { 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 resourceConfig = this._resourceConfig = cloneDeep(options.resourceConfig); var provider = options.templateProvider; + // Load templates var templates = this._templates = ['index', 'resource', 'resource_new'].reduce(function(cache, name) { @@ -37,6 +40,7 @@ function OKAdminView(options) { cache[name] = template; return cache; }, {}); + // OKAdmin middleware is a router, so mounts on 'use' Object.defineProperty(this, 'mount', { value: 'use', @@ -120,13 +124,14 @@ function OKAdminView(options) { resource: resource, query: id }); - fetchResourceTemplateData(meta, query, getResourceTemplateData).then(function(data) { - if (!data) { - resourceMissingHandler(req, res)() - } else { - view.renderResource(req, res, data); - } - }).fail(errorHandler(req, res)); + fetchResourceTemplateData(meta, query, getResourceTemplateData) + .then(function(data) { + if (!data) { + resourceMissingHandler(req, res)() + } else { + view.renderResource(req, res, data); + } + }).fail(errorHandler(req, res)); } }); @@ -201,6 +206,7 @@ function getResourceTemplateData(meta, resource, data) { meta = meta || {}; resource = resource || {}; data = data || {}; + // Decorate spec with actual resource values var spec = Object.keys(resource.spec).reduce(function(cache, prop) { var value = data[prop]; cache[prop].value = value; @@ -245,7 +251,7 @@ OKAdminView.prototype.renderResourceNew = function(req, res, data) { * Annotate template data with schema info */ function fetchIndexTemplateData(meta, queries) { - return Q.Promise(function(resolve, reject) { + return Q.promise(function(resolve, reject) { Q.all([meta.get()].concat(queries.map(function(query) { return query.get(); }))).then(function(results) { @@ -293,7 +299,7 @@ function fetchIndexTemplateData(meta, queries) { */ function fetchResourceTemplateData(meta, query, fn) { fn = fn || function(m, r, d) { return {meta: m, resource: d}; }; - return Q.Promise(function(resolve, reject) { + return Q.promise(function(resolve, reject) { meta.get().then(function(metadata) { query.get().then(function(data) { var resource = query.resource; diff --git a/app/node_modules/okadminview/package.json b/app/node_modules/okadminview/package.json index 4832db1..c428645 100644 --- a/app/node_modules/okadminview/package.json +++ b/app/node_modules/okadminview/package.json @@ -10,6 +10,7 @@ "license": "None", "dependencies": { "body-parser": "^1.12.2", + "lodash.clonedeep": "^3.0.0", "method-override": "^2.3.2", "object-assign": "^2.0.0", "pluralize": "^1.1.2", diff --git a/app/node_modules/okquery/index.js b/app/node_modules/okquery/index.js index 2d93e2a..89c8b73 100644 --- a/app/node_modules/okquery/index.js +++ b/app/node_modules/okquery/index.js @@ -1,3 +1,4 @@ +var cloneDeep = require('lodash.clonedeep'); var assign = require('object-assign'); var isobject = require('lodash.isobject'); var Q = require('q'); @@ -16,6 +17,9 @@ function OKQuery(options) { var resource = options.resource; var type = resource.type; var query = options.query || '*'; + // Ensure immutability + if (isobject(query)) + query = cloneDeep(query); Object.defineProperty(this, 'resource', { value: resource, @@ -55,6 +59,9 @@ function createQuery(resource, query, options) { function queryComplex(resource, query) { var dynamicProp; + // Query is an object specifying key value pairs against which + // to match DB entries. Iterate through and check if any of the values + // is unbound e.g. :id var notDynamic = Object.keys(query).every(function(prop) { var matcher = query[prop]; if (isDynamic(matcher)) { @@ -71,6 +78,8 @@ function queryComplex(resource, query) { } } else { return function(id) { + // Bind the dynamic property to its value + // and add the pair to the query var dynamicQuery = {}; dynamicQuery[dynamicProp] = id; var query = assign({}, query, dynamicQuery); diff --git a/app/node_modules/okquery/package.json b/app/node_modules/okquery/package.json index 606d45b..5ba9dd5 100644 --- a/app/node_modules/okquery/package.json +++ b/app/node_modules/okquery/package.json @@ -9,6 +9,7 @@ "author": "OKFocus", "license": "None", "dependencies": { + "lodash.clonedeep": "^3.0.0", "lodash.isobject": "^3.0.1", "object-assign": "^2.0.0", "q": "^1.2.0" diff --git a/app/node_modules/okserver/index.js b/app/node_modules/okserver/index.js index 1645eaa..cf06b3c 100644 --- a/app/node_modules/okserver/index.js +++ b/app/node_modules/okserver/index.js @@ -67,16 +67,6 @@ function OKServer(options) { // Make sure this lady is last. Checks whether the desired // route has a trailing-slash counterpart and redirects there app.use(slash()); - - /** - * Create a handler which redirect all requests to - * the same route with a trailing slash appended - */ - function redirect(routeNoSlash) { - return function(req, res) { - res.redirect(301, routeNoSlash + '/'); - } - } } OKServer.prototype.listen = function listen(port) { -- cgit v1.2.3-70-g09d2 From 00fd19b33ead56842daeb6f6a24735a8687f7744 Mon Sep 17 00:00:00 2001 From: Sean Fridman Date: Thu, 9 Apr 2015 20:58:32 -0400 Subject: Implement flash messaging for admin success/error stuff --- app/node_modules/okadminview/index.js | 25 +++++++++++++++++++++++-- app/node_modules/okadminview/package.json | 2 ++ themes/okadmin/templates/index.liquid | 2 ++ themes/okadmin/templates/partials/errors.liquid | 10 ---------- themes/okadmin/templates/partials/flash.liquid | 14 ++++++++++++++ themes/okadmin/templates/resource.liquid | 2 +- themes/okadmin/templates/resource_new.liquid | 2 +- 7 files changed, 43 insertions(+), 14 deletions(-) delete mode 100644 themes/okadmin/templates/partials/errors.liquid create mode 100644 themes/okadmin/templates/partials/flash.liquid (limited to 'app/node_modules') diff --git a/app/node_modules/okadminview/index.js b/app/node_modules/okadminview/index.js index 05e2251..9e9aacf 100644 --- a/app/node_modules/okadminview/index.js +++ b/app/node_modules/okadminview/index.js @@ -2,6 +2,8 @@ var assign = require('object-assign'); var cloneDeep = require('lodash.clonedeep'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); +var session = require('express-session'); +var flash = require('connect-flash'); var Q = require('q'); var pluralize = require('pluralize'); var OKQuery = require('okquery'); @@ -75,6 +77,14 @@ function OKAdminView(options) { strict: app.get('strict routing') }); + // Enable basic sessions for flash messages + router.use(session({ + secret: 'okadmin', + resave: false, + saveUninitialized: false + })); + // Enable flash messaging + router.use(flash()); // Parse form data router.use(bodyParser.urlencoded({extended: true})); // HTML forms only support POST and GET methods @@ -91,7 +101,10 @@ function OKAdminView(options) { router.get('/', function readIndex(req, res, next) { fetchIndexTemplateData(meta, indexQueries).then(function(data) { - view.renderIndex(req, res, data); + view.renderIndex(req, res, assign(data, { + success: req.flash('success'), + errors: req.flash('errors') + })); }).fail(errorHandler(req, res)); }); @@ -103,6 +116,8 @@ function OKAdminView(options) { } else { meta.get().then(function(metadata) { view.renderResourceNew(req, res, { + success: req.flash('success'), + errors: req.flash('errors'), meta: metadata, resource: { type: resource.type, @@ -129,7 +144,10 @@ function OKAdminView(options) { if (!data) { resourceMissingHandler(req, res)() } else { - view.renderResource(req, res, data); + view.renderResource(req, res, assign(data, { + success: req.flash('success'), + errors: req.flash('errors') + })); } }).fail(errorHandler(req, res)); } @@ -147,6 +165,7 @@ function OKAdminView(options) { try { resource.assertValid(data); resource.create(data).then(function(created) { + req.flash('success', 'Created ' + type); res.redirect(303, data[resource.idField]); }).fail(errorHandler(req, res)); } catch (errors) { @@ -170,6 +189,7 @@ function OKAdminView(options) { try { resource.assertValid(data); resource.update(id, data).then(function(updated) { + req.flash('success', 'Updated ' + type); res.redirect(303, '../' + updated[resource.idField]); }).fail(errorHandler(req, res)); } catch (errors) { @@ -189,6 +209,7 @@ function OKAdminView(options) { } else { meta.get().then(function(metadata) { resource.destroy(id).then(function() { + req.flash('success', 'Deleted ' + type); res.redirect(303, '../..'); }).fail(errorHandler(req, res)); }).fail(errorHandler(req, res)); diff --git a/app/node_modules/okadminview/package.json b/app/node_modules/okadminview/package.json index c428645..4c6d11c 100644 --- a/app/node_modules/okadminview/package.json +++ b/app/node_modules/okadminview/package.json @@ -10,6 +10,8 @@ "license": "None", "dependencies": { "body-parser": "^1.12.2", + "connect-flash": "^0.1.1", + "express-session": "^1.11.1", "lodash.clonedeep": "^3.0.0", "method-override": "^2.3.2", "object-assign": "^2.0.0", diff --git a/themes/okadmin/templates/index.liquid b/themes/okadmin/templates/index.liquid index 95c64dd..a5b27e5 100644 --- a/themes/okadmin/templates/index.liquid +++ b/themes/okadmin/templates/index.liquid @@ -1,5 +1,7 @@ {% include 'partials/head' %} +{% include 'partials/flash' %} +
{% for pair in resources %} {% assign name = pair[0] %} diff --git a/themes/okadmin/templates/partials/errors.liquid b/themes/okadmin/templates/partials/errors.liquid deleted file mode 100644 index cdb0b25..0000000 --- a/themes/okadmin/templates/partials/errors.liquid +++ /dev/null @@ -1,10 +0,0 @@ -
- {% for error in errors %} -
-
{{error.message}}
-
- Expected {{error.expected}} but got {{error.actual}} -
-
- {% endfor %} -
diff --git a/themes/okadmin/templates/partials/flash.liquid b/themes/okadmin/templates/partials/flash.liquid new file mode 100644 index 0000000..33b621b --- /dev/null +++ b/themes/okadmin/templates/partials/flash.liquid @@ -0,0 +1,14 @@ +{% if success %} +
+ {{success}} +
+{% elsif errors %} +
+ {% for error in errors %} +
+
{{error.message}}
+
+ {% endfor %} +
+{% endif %} + diff --git a/themes/okadmin/templates/resource.liquid b/themes/okadmin/templates/resource.liquid index c321e8a..53cd83e 100644 --- a/themes/okadmin/templates/resource.liquid +++ b/themes/okadmin/templates/resource.liquid @@ -1,6 +1,6 @@ {% include 'partials/head' %} -{% include 'partials/errors' %} +{% include 'partials/flash' %}