|
// Exposed Controller |
|
|
|
// Lines 2-8 are the basic necessities for extending a collection. |
|
var Collection = require('deployd/lib/resources/collection'); |
|
var util = require('util'); |
|
function ExposedCollection(name, options) { |
|
Collection.apply(this, arguments); |
|
|
|
var config = this.config; |
|
|
|
if(!this.properties) { |
|
this.properties = {}; |
|
} |
|
} |
|
|
|
ExposedCollection.external = Collection.external; |
|
util.inherits(ExposedCollection, Collection); |
|
|
|
// Additional modules used below. |
|
var path = require('path'); |
|
|
|
// Label for the type in Deployd's dashboard add button. |
|
ExposedCollection.label = "Exposed Collection"; |
|
|
|
// Use the same events as Collection |
|
ExposedCollection.events = Collection.events; |
|
|
|
// Make sure we show up in Deployd's dashboard. |
|
ExposedCollection.prototype.clientGeneration = true; |
|
|
|
ExposedCollection.dashboard = Collection.dashboard; |
|
|
|
// I've not put any ExposedColleciton.prototype.handle so it will use |
|
// the one from Collection.prototype. |
|
|
|
/** |
|
* Find all the objects in a collection that match the given |
|
* query. Then execute its get script using each object. |
|
* |
|
* @param {Context} ctx |
|
* @param {Function} fn(err, result) |
|
*/ |
|
|
|
ExposedCollection.prototype.find = function (ctx, fn) { |
|
var collection = this |
|
, store = this.store |
|
, query = ctx.query || {} |
|
, session = ctx.session |
|
, client = ctx.dpd |
|
, errors |
|
, data |
|
, sanitizedQuery = this.sanitizeQuery(query); |
|
|
|
function done(err, result) { |
|
// debug("Get listener called back with", err || result); |
|
if(typeof query.id === 'string' && (result && result.length === 0) || !result) { |
|
err = err || { |
|
message: 'not found', |
|
statusCode: 404 |
|
}; |
|
// debug('could not find object by id %s', query.id); |
|
} |
|
if(err) { |
|
return fn(err); |
|
} |
|
if(typeof query.id === 'string' && Array.isArray(result)) { |
|
return fn(null, result[0]); |
|
} |
|
|
|
fn(null, result); |
|
} |
|
|
|
// debug('finding %j; sanitized %j', query, sanitizedQuery); |
|
|
|
store.find(sanitizedQuery, function (err, result) { |
|
// debug("Find Callback"); |
|
if(err) return done(err); |
|
// debug('found %j', err || result || 'none'); |
|
if(!collection.shouldRunEvent(collection.events.Get, ctx)) { |
|
return done(err, result); |
|
} |
|
|
|
var errors = {}; |
|
|
|
if(Array.isArray(result)) { |
|
|
|
var remaining = result && result.length; |
|
if(!remaining) return done(err, result); |
|
result.forEach(function (data) { |
|
// domain for onGet event scripts |
|
var domain = createDomain(data, errors, ctx); |
|
|
|
// console.log(ctx); |
|
|
|
collection.events.Get.run(ctx, domain, function (err) { |
|
if (err) { |
|
if (err instanceof Error) { |
|
return done(err); |
|
} else { |
|
errors[data.id] = err; |
|
} |
|
} |
|
|
|
remaining--; |
|
if(!remaining) { |
|
done(null, result.filter(function(r) { |
|
return !errors[r.id]; |
|
})); |
|
} |
|
}); |
|
}); |
|
} else { |
|
// domain for onGet event scripts |
|
data = result; |
|
var domain = createDomain(data, errors, ctx); |
|
|
|
collection.events.Get.run(ctx, domain, function (err) { |
|
if(err) return done(err); |
|
|
|
done(null, data); |
|
}); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Execute a `delete` event script, if one exists, using each object found. |
|
* Then remove a single object that matches the `ctx.query.id`. Finally call |
|
* `fn(err)` passing an `error` if one occurred. |
|
* |
|
* @param {Context} ctx |
|
* @param {Function} fn(err) |
|
*/ |
|
|
|
Collection.prototype.remove = function (ctx, fn) { |
|
var collection = this |
|
, store = this.store |
|
, session = ctx.session |
|
, query = ctx.query |
|
, sanitizedQuery = this.sanitizeQuery(query) |
|
, errors; |
|
|
|
if(!(query && query.id)) return fn('You must include a query with an id when deleting an object from a collection.'); |
|
store.find(sanitizedQuery, function (err, result) { |
|
if(err) { |
|
return fn(err); |
|
} |
|
|
|
function done(err) { |
|
if(err) return fn(err); |
|
store.remove(sanitizedQuery, fn); |
|
if(session.emitToAll) session.emitToAll(collection.name + ':changed'); |
|
} |
|
|
|
if(collection.shouldRunEvent(collection.events.Delete, ctx)) { |
|
var domain = createDomain(result, errors, ctx); |
|
|
|
domain['this'] = domain.data = result; |
|
collection.events.Delete.run(ctx, domain, done); |
|
} else { |
|
done(); |
|
} |
|
}); |
|
}; |
|
|
|
|
|
/** |
|
* Execute the onPost or onPut listener. If it succeeds, |
|
* save the given item in the collection. |
|
* |
|
* @param {Context} ctx |
|
* @param {Function} fn(err, result) |
|
*/ |
|
|
|
Collection.prototype.save = function (ctx, fn) { |
|
var collection = this |
|
, store = this.store |
|
, session = ctx.session |
|
, item = ctx.body |
|
|
|
, query = ctx.query || {} |
|
, client = ctx.dpd |
|
, errors = {}; |
|
|
|
if(!item) return done('You must include an object when saving or updating.'); |
|
|
|
// build command object |
|
var commands = {}; |
|
Object.keys(item).forEach(function (key) { |
|
if(item[key] && typeof item[key] === 'object' && !Array.isArray(item[key])) { |
|
Object.keys(item[key]).forEach(function (k) { |
|
if(k[0] == '$') { |
|
commands[key] = item[key]; |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
item = this.sanitize(item); |
|
|
|
// handle id on either body or query |
|
if(item.id) { |
|
query.id = item.id; |
|
} |
|
|
|
// debug('saving %j with id %s', item, query.id); |
|
|
|
function done(err, item) { |
|
errors = domain && domain.hasErrors() && {errors: errors}; |
|
// debug('errors: %j', err); |
|
fn(errors || err, item); |
|
} |
|
|
|
var domain = createDomain(item, errors, ctx); |
|
|
|
domain.protect = function(property) { |
|
delete domain.data[property]; |
|
}; |
|
|
|
domain.changed = function (property) { |
|
if(domain.data.hasOwnProperty(property)) { |
|
if(domain.previous && domain.previous[property] === domain.data[property]) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
return false; |
|
}; |
|
|
|
domain.previous = {}; |
|
|
|
function put() { |
|
var id = query.id |
|
, sanitizedQuery = collection.sanitizeQuery(query) |
|
, prev = {}; |
|
|
|
store.first(sanitizedQuery, function(err, obj) { |
|
if(!obj) { |
|
if (Object.keys(sanitizedQuery) === 1) { |
|
return done(new Error("No object exists with that id")); |
|
} else { |
|
return done(new Error("No object exists that matches that query")); |
|
} |
|
} |
|
if(err) return done(err); |
|
|
|
// copy previous obj |
|
Object.keys(obj).forEach(function (key) { |
|
prev[key] = obj[key]; |
|
}); |
|
|
|
// merge changes |
|
Object.keys(item).forEach(function (key) { |
|
obj[key] = item[key]; |
|
}); |
|
|
|
prev.id = id; |
|
item = obj; |
|
domain['this'] = item; |
|
domain.data = item; |
|
domain.previous = prev; |
|
|
|
collection.execCommands('update', item, commands); |
|
|
|
var errs = collection.validate(item); |
|
|
|
if(errs) return done({errors: errs}); |
|
|
|
function runPutEvent(err) { |
|
if(err) { |
|
return done(err); |
|
} |
|
|
|
if(collection.shouldRunEvent(collection.events.Put, ctx)) { |
|
collection.events.Put.run(ctx, domain, commit); |
|
} else { |
|
commit(); |
|
} |
|
} |
|
|
|
function commit(err) { |
|
if(err || domain.hasErrors()) { |
|
return done(err || errors); |
|
} |
|
|
|
delete item.id; |
|
store.update({id: query.id}, item, function (err) { |
|
if(err) return done(err); |
|
item.id = id; |
|
|
|
done(null, item); |
|
|
|
if(session && session.emitToAll) session.emitToAll(collection.name + ':changed'); |
|
}); |
|
} |
|
|
|
if (collection.shouldRunEvent(collection.events.Validate, ctx)) { |
|
collection.events.Validate.run(ctx, domain, function (err) { |
|
if(err || domain.hasErrors()) return done(err || errors); |
|
runPutEvent(err); |
|
}); |
|
} else { |
|
runPutEvent(); |
|
} |
|
}); |
|
} |
|
|
|
function post() { |
|
var errs = collection.validate(item, true); |
|
|
|
if(errs) return done({errors: errs}); |
|
|
|
// generate id before event listener |
|
item.id = store.createUniqueIdentifier(); |
|
|
|
if(collection.shouldRunEvent(collection.events.Post, ctx)) { |
|
collection.events.Post.run(ctx, domain, function (err) { |
|
if(err) { |
|
// debug('onPost script error %j', err); |
|
return done(err); |
|
} |
|
if(err || domain.hasErrors()) return done(err || errors); |
|
// debug('inserting item', item); |
|
store.insert(item, done); |
|
if(session && session.emitToAll) session.emitToAll(collection.name + ':changed'); |
|
}); |
|
} else { |
|
store.insert(item, done); |
|
if(session && session.emitToAll) session.emitToAll(collection.name + ':changed'); |
|
} |
|
} |
|
|
|
if (query.id) { |
|
put(); |
|
} else if (collection.shouldRunEvent(collection.events.Validate, ctx)) { |
|
collection.events.Validate.run(ctx, domain, function (err) { |
|
if(err || domain.hasErrors()) return done(err || errors); |
|
post(); |
|
}); |
|
} else { |
|
post(); |
|
} |
|
}; |
|
|
|
|
|
// Modify this function to expand the scripts domain to include |
|
// any context (ctx) variables you would like. |
|
var createDomain = function (data, errors, ctx) { |
|
console.log("I'm new here!"); |
|
|
|
var hasErrors = false; |
|
var domain = { |
|
error: function(key, val) { |
|
// debug('error %s %s', key, val); |
|
errors[key] = val || true; |
|
hasErrors = true; |
|
}, |
|
errorIf: function(condition, key, value) { |
|
if (condition) { |
|
domain.error(key, value); |
|
} |
|
}, |
|
errorUnless: function(condition, key, value) { |
|
domain.errorIf(!condition, key, value); |
|
}, |
|
hasErrors: function() { |
|
return hasErrors; |
|
}, |
|
hide: function(property) { |
|
delete domain.data[property]; |
|
}, |
|
'this': data, |
|
data: data, |
|
// Added headers to the domain. |
|
headers:ctx.req.headers |
|
}; |
|
return domain; |
|
} |
|
|
|
// Expose custom methods in dpd.js on the client. |
|
ExposedCollection.prototype.clientGenerationExec = ['find', 'create', 'cancel', 'refund', 'capture', 'modify']; |
|
|
|
module.exports = ExposedCollection; |