function buildApi (service, urlRoot, router, logger, middleware, verbs) { const hooks = { 'create:request': createPipe(), 'create:response': createPipe(), 'read:response': createPipe(), 'update:request': createPipe(), 'update:response': createPipe(), 'partialUpdate:request': createPipe(), 'partialUpdate:response': createPipe() } class Api extends EventEmitter { hook (name, fn) { if (!hooks[name]) throw new Error('No hook exists for: ' + name) hooks[name].add(fn) } } const api = new Api() if (!Array.isArray(middleware) && typeof middleware !== 'function') throw new Error('Middleware is not defined') // Support all verbs by default if (!Array.isArray(verbs)) verbs = [ 'get', 'post', 'put', 'patch', 'delete' ] // Create endpoints verbs.forEach(verb => { routes[verb](service, urlRoot, router, logger, middleware, api.emit.bind(api), hooks) }) return api }
module.exports = function CrudService(name, save, schema, options) { var slug = (options && options.slug) ? options.slug : name.toLowerCase().replace(/ /g, '') , plural = (options && options.plural) ? options.plural : name + 's' , self = new events.EventEmitter() var pre = { create: Pipe.createPipe(), createValidate: Pipe.createPipe(), update: Pipe.createPipe(), updateValidate: Pipe.createPipe(), partialUpdate: Pipe.createPipe(), partialValidate: Pipe.createPipe(), 'delete': Pipe.createPipe() } if (schema.schema[save.idProperty] === undefined) { throw new Error('schema does not have the required property \'' + save.idProperty + '\'') } return extend(self, { name: name, slug: slug, plural: plural, schema: schema, idProperty: save.idProperty, create: function (object, validateOptions, callback) { if (typeof validateOptions === 'function') { callback = validateOptions } callback = callback || emptyFn var cleanObject = schema.cast(schema.stripUnknownProperties(schema.makeDefault(object), validateOptions.tag)) pre.createValidate.run(cleanObject, function (error, pipedObject) { if (error) { return callback(error) } schema.validate(pipedObject, validateOptions.set, validateOptions.tag, function (error, validationErrors) { if (error) { return callback(error) } if (Object.keys(validationErrors).length > 0) { var validationError = new Error('Validation Error') validationError.errors = validationErrors return callback(validationError, pipedObject) } pre.create.run(pipedObject, function (error, pipedObject) { if (error) { return callback(error) } save.create(pipedObject, function (error, savedObject) { if (error) { return callback(error) } self.emit('create', savedObject) callback(undefined, savedObject) }) }) }) }) }, read: function(id, callback) { return save.read(schema.castProperty(schema.schema[save.idProperty].type, id), callback) }, update: function (object, validateOptions, callback) { callback = callback || emptyFn var cleanObject = schema.cast(schema.stripUnknownProperties(schema.makeDefault(object), validateOptions.tag)) pre.updateValidate.run(cleanObject, function (error, pipedObject) { if (error) { return callback(error) } schema.validate(pipedObject, validateOptions.set, validateOptions.tag, function (error, validationErrors) { if (error) { return callback(error) } if (Object.keys(validationErrors).length > 0) { var validationError = new Error('Validation Error') validationError.errors = validationErrors return callback(validationError, pipedObject) } pre.update.run(pipedObject, function (error, pipedObject) { if (error) { return callback(error, pipedObject) } save.update(pipedObject, function (error, savedObject) { if (error) { return callback(error) } self.emit('update', savedObject) callback(undefined, savedObject) }) }) }) }) }, partialUpdate: function (object, validateOptions, callback) { callback = callback || emptyFn if (typeof validateOptions === 'function') { callback = validateOptions } if (!object[save.idProperty]) { return callback(new Error('object have not ID property \'' + save.idProperty + '\'')) } save.read(object[save.idProperty], function (error, readObject) { if (error) { return callback(error) } if (typeof readObject === 'undefined') { return callback(new Error('Couldn\'t find object with an ' + save.idProperty + ' of ' + object[save.idProperty])) } readObject = extend(readObject, object) var cleanObject = schema.cast(schema.stripUnknownProperties(readObject, validateOptions.tag)) pre.partialUpdate.run(cleanObject, function (error, pipedObject) { if (error) { return callback(error) } schema.validate(pipedObject, validateOptions.set, validateOptions.tag, function (error, validationErrors) { if (error) { return callback(error) } if (Object.keys(validationErrors).length > 0) { var validationError = new Error('Validation Error') validationError.errors = validationErrors return callback(validationError, pipedObject) } pre.partialUpdate.run(pipedObject, function (error, pipedObject) { if (error) { return callback(error, pipedObject) } // Now only update the keys the original object had. var objectForUpdate = {} Object.keys(object).forEach(function (key) { objectForUpdate[key] = pipedObject[key] }) save.update(objectForUpdate, function (error, savedObject) { if (error) { return callback(error) } self.emit('partialUpdate', savedObject) callback(undefined, savedObject) }) }) }) }) }) }, 'delete': function (id, callback) { save['delete'](id, function (error) { if (error) { return callback(error) } self.emit('delete', id) if (typeof callback === 'function') callback() }) }, deleteMany: function (query, callback) { save.deleteMany(query, function (error) { if (error) { return callback(error) } self.emit('deleteMany', query) callback() }) }, count: save.count, find: save.find, pre: function (method, processor) { return pre[method].add(processor) } }) }