From a384a55c1918cd16ddcadc91f12cd16c1846663f Mon Sep 17 00:00:00 2001 From: mountain1234585 Date: Mon, 11 Jul 2016 15:53:40 +0530 Subject: [PATCH] Add files via upload --- lib/persisted-model.js | 351 ++++++++++++++++------------------------- 1 file changed, 136 insertions(+), 215 deletions(-) diff --git a/lib/persisted-model.js b/lib/persisted-model.js index 2296067dd..ba02beb34 100644 --- a/lib/persisted-model.js +++ b/lib/persisted-model.js @@ -1,8 +1,3 @@ -// Copyright IBM Corp. 2014,2016. All Rights Reserved. -// Node module: loopback -// This file is licensed under the MIT License. -// License text available at https://opensource.org/licenses/MIT - /*! * Module Dependencies. */ @@ -92,7 +87,7 @@ module.exports = function(registry) { /** * Create new instance of Model, and save to database. * - * @param {Object|Object[]} [data] Optional data argument. Can be either a single model instance or an array of instances. + * @param {Object}|[{Object}] data Optional data argument. Can be either a single model instance or an array of instances. * * @callback {Function} callback Callback function called with `cb(err, obj)` signature. * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). @@ -111,27 +106,13 @@ module.exports = function(registry) { * @param {Object} model Updated model instance. */ - PersistedModel.upsert = PersistedModel.updateOrCreate = PersistedModel.patchOrCreate = - function upsert(data, callback) { + PersistedModel.upsert = PersistedModel.updateOrCreate = function upsert(data, callback) { throwNotAttached(this.modelName, 'upsert'); }; - /** - * Replace or insert a model instance; replace existing record if one is found, - * such that parameter `data.id` matches `id` of model instance; otherwise, - * insert a new record. - * @param {Object} data The model instance data. - * @options {Object} [options] Options for replaceOrCreate - * @property {Boolean} validate Perform validation before saving. Default is true. - * @callback {Function} callback Callback function called with `cb(err, obj)` signature. - * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). - * @param {Object} model Replaced model instance. - */ - - PersistedModel.replaceOrCreate = function replaceOrCreate(data, callback) { - throwNotAttached(this.modelName, 'replaceOrCreate'); + PersistedModel.upsertWithWhere = function upsertWithWhere(where, data, callback) { + throwNotAttached(this.modelName, 'upsertWithWhere'); }; - /** * Finds one record matching the optional filter object. If not found, creates * the object using the data provided as second argument. In this sense it is @@ -160,7 +141,7 @@ module.exports = function(registry) { * @callback {Function} callback Callback function called with `cb(err, instance, created)` arguments. Required. * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). * @param {Object} instance Model instance matching the `where` filter, if found. - * @param {Boolean} created True if the instance does not exist and gets created. + * @param {Boolean} created True if the instance matching the `where` filter was created. */ PersistedModel.findOrCreate = function findOrCreate(query, data, callback) { @@ -197,7 +178,7 @@ module.exports = function(registry) { * @param {Object} instance Model instance matching the specified ID or null if no instance matches. */ - PersistedModel.findById = function findById(id, filter, cb) { + PersistedModel.findById = function find(id, filter, cb) { throwNotAttached(this.modelName, 'findById'); }; @@ -384,7 +365,7 @@ module.exports = function(registry) { } callback = callback || function() { - }; + }; options = options || {}; if (!('validate' in options)) { @@ -495,45 +476,10 @@ module.exports = function(registry) { * @param {Object} instance Updated instance. */ - PersistedModel.prototype.updateAttributes = PersistedModel.prototype.patchAttributes = - function updateAttributes(data, cb) { + PersistedModel.prototype.updateAttributes = function updateAttributes(data, cb) { throwNotAttached(this.modelName, 'updateAttributes'); }; - /** - * Replace attributes for a model instance and persist it into the datasource. - * Performs validation before replacing. - * - * @param {Object} data Data to replace. - * @options {Object} [options] Options for replace - * @property {Boolean} validate Perform validation before saving. Default is true. - * @callback {Function} callback Callback function called with `(err, instance)` arguments. - * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). - * @param {Object} instance Replaced instance. - */ - - PersistedModel.prototype.replaceAttributes = function replaceAttributes(data, cb) { - throwNotAttached(this.modelName, 'replaceAttributes'); - }; - - /** - * Replace attributes for a model instance whose id is the first input - * argument and persist it into the datasource. - * Performs validation before replacing. - * - * @param {*} id The ID value of model instance to replace. - * @param {Object} data Data to replace. - * @options {Object} [options] Options for replace - * @property {Boolean} validate Perform validation before saving. Default is true. - * @callback {Function} callback Callback function called with `(err, instance)` arguments. - * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). - * @param {Object} instance Replaced instance. - */ - - PersistedModel.replaceById = function replaceById(id, data, cb) { - throwNotAttached(this.modelName, 'replaceById'); - }; - /** * Reload object from persistence. Requires `id` member of `object` to be able to call `find`. * @callback {Function} callback Callback function called with `(err, instance)` arguments. Required. @@ -602,9 +548,6 @@ module.exports = function(registry) { var typeName = PersistedModel.modelName; var options = PersistedModel.settings; - // This is just for LB 3.x - options.replaceOnPUT = options.replaceOnPUT !== false; - function setRemoting(scope, name, options) { var fn = scope[name]; fn._delegate = true; @@ -615,50 +558,41 @@ module.exports = function(registry) { setRemoting(PersistedModel, 'create', { description: 'Create a new instance of the model and persist it into the data source.', accessType: 'WRITE', - accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description: - 'Model instance data' }, - returns: { arg: 'data', type: typeName, root: true }, - http: { verb: 'post', path: '/' }, + accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'post', path: '/'} }); - var upsertOptions = { - aliases: ['upsert', 'updateOrCreate'], - description: 'Patch an existing model instance or insert a new one into the data source.', + setRemoting(PersistedModel, 'upsert', { + aliases: ['updateOrCreate'], + description: 'Update an existing model instance or insert a new one into the data source.', accessType: 'WRITE', - accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description: - 'Model instance data' }, - returns: { arg: 'data', type: typeName, root: true }, - http: [{ verb: 'patch', path: '/' }], - }; - - if (!options.replaceOnPUT) { - upsertOptions.http.push({ verb: 'put', path: '/' }); - } - setRemoting(PersistedModel, 'patchOrCreate', upsertOptions); - - var replaceOrCreateOptions = { - description: 'Replace an existing model instance or insert a new one into the data source.', + accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'put', path: '/'} + }); + setRemoting(PersistedModel, 'upsertWithWhere', { + aliases: ['upsertWithWhere'], + description: 'Update an existing model instance or insert a new one into the data source based on the non-primary ' + + 'key filter.', accessType: 'WRITE', - accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description: - 'Model instance data' }, - returns: { arg: 'data', type: typeName, root: true }, - http: [{ verb: 'post', path: '/replaceOrCreate' }], - }; - - if (options.replaceOnPUT) { - replaceOrCreateOptions.http.push({ verb: 'put', path: '/' }); - } - - setRemoting(PersistedModel, 'replaceOrCreate', replaceOrCreateOptions); - + accepts: [ + {arg: 'where', type: 'object', http: {source: 'query'}, + description: 'Criteria to match model instances'}, + {arg: 'data', type: 'object', http: {source: 'body'}, + description: 'An object of model property name/value pairs'}, + ], + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'put', path: '/upsertWithWhere'} + }); setRemoting(PersistedModel, 'exists', { description: 'Check whether a model instance exists in the data source.', accessType: 'READ', - accepts: { arg: 'id', type: 'any', description: 'Model id', required: true }, - returns: { arg: 'exists', type: 'boolean' }, + accepts: {arg: 'id', type: 'any', description: 'Model id', required: true}, + returns: {arg: 'exists', type: 'boolean'}, http: [ - { verb: 'get', path: '/:id/exists' }, - { verb: 'head', path: '/:id' }, + {verb: 'get', path: '/:id/exists'}, + {verb: 'head', path: '/:id'} ], rest: { // After hook to map exists to 200/404 for HEAD @@ -678,8 +612,8 @@ module.exports = function(registry) { } else { cb(); } - }, - }, + } + } }); setRemoting(PersistedModel, 'findById', { @@ -687,66 +621,44 @@ module.exports = function(registry) { accessType: 'READ', accepts: [ { arg: 'id', type: 'any', description: 'Model id', required: true, - http: { source: 'path' }}, + http: {source: 'path'}}, { arg: 'filter', type: 'object', - description: 'Filter defining fields and include' }, + description: 'Filter defining fields and include'} ], - returns: { arg: 'data', type: typeName, root: true }, - http: { verb: 'get', path: '/:id' }, - rest: { after: convertNullToNotFoundError }, + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'get', path: '/:id'}, + rest: {after: convertNullToNotFoundError} }); - var replaceByIdOptions = { - description: 'Replace attributes for a model instance and persist it into the data source.', - accessType: 'WRITE', - accepts: [ - { arg: 'id', type: 'any', description: 'Model id', required: true, - http: { source: 'path' }}, - { arg: 'data', type: 'object', http: { source: 'body' }, description: - 'Model instance data' }, - ], - returns: { arg: 'data', type: typeName, root: true }, - http: [{ verb: 'post', path: '/:id/replace' }], - }; - - if (options.replaceOnPUT) { - replaceByIdOptions.http.push({ verb: 'put', path: '/:id' }); - } - - setRemoting(PersistedModel, 'replaceById', replaceByIdOptions); - - setRemoting(PersistedModel, 'find', { description: 'Find all instances of the model matched by filter from the data source.', accessType: 'READ', - accepts: { arg: 'filter', type: 'object', description: - 'Filter defining fields, where, include, order, offset, and limit' }, - returns: { arg: 'data', type: [typeName], root: true }, - http: { verb: 'get', path: '/' }, + accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'}, + returns: {arg: 'data', type: [typeName], root: true}, + http: {verb: 'get', path: '/'} }); setRemoting(PersistedModel, 'findOne', { description: 'Find first instance of the model matched by filter from the data source.', accessType: 'READ', - accepts: { arg: 'filter', type: 'object', description: - 'Filter defining fields, where, include, order, offset, and limit' }, - returns: { arg: 'data', type: typeName, root: true }, - http: { verb: 'get', path: '/findOne' }, - rest: { after: convertNullToNotFoundError }, + accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'}, + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'get', path: '/findOne'}, + rest: {after: convertNullToNotFoundError} }); setRemoting(PersistedModel, 'destroyAll', { description: 'Delete all matching records.', accessType: 'WRITE', - accepts: { arg: 'where', type: 'object', description: 'filter.where object' }, + accepts: {arg: 'where', type: 'object', description: 'filter.where object'}, returns: { arg: 'count', type: 'object', description: 'The number of instances deleted', - root: true, + root: true }, - http: { verb: 'del', path: '/' }, - shared: false, + http: {verb: 'del', path: '/'}, + shared: false }); setRemoting(PersistedModel, 'updateAll', { @@ -754,78 +666,69 @@ module.exports = function(registry) { description: 'Update instances of the model matched by where from the data source.', accessType: 'WRITE', accepts: [ - { arg: 'where', type: 'object', http: { source: 'query' }, - description: 'Criteria to match model instances' }, - { arg: 'data', type: 'object', http: { source: 'body' }, - description: 'An object of model property name/value pairs' }, + {arg: 'where', type: 'object', http: {source: 'query'}, + description: 'Criteria to match model instances'}, + {arg: 'data', type: 'object', http: {source: 'body'}, + description: 'An object of model property name/value pairs'}, ], returns: { arg: 'count', description: 'The number of instances updated', type: 'object', - root: true, + root: true }, - http: { verb: 'post', path: '/update' }, + http: {verb: 'post', path: '/update'} }); setRemoting(PersistedModel, 'deleteById', { aliases: ['destroyById', 'removeById'], description: 'Delete a model instance by id from the data source.', accessType: 'WRITE', - accepts: { arg: 'id', type: 'any', description: 'Model id', required: true, - http: { source: 'path' }}, - http: { verb: 'del', path: '/:id' }, - returns: { arg: 'count', type: 'object', root: true }, + accepts: {arg: 'id', type: 'any', description: 'Model id', required: true, + http: {source: 'path'}}, + http: {verb: 'del', path: '/:id'}, + returns: {arg: 'count', type: 'object', root: true} }); setRemoting(PersistedModel, 'count', { description: 'Count instances of the model matched by where from the data source.', accessType: 'READ', - accepts: { arg: 'where', type: 'object', description: 'Criteria to match model instances' }, - returns: { arg: 'count', type: 'number' }, - http: { verb: 'get', path: '/count' }, + accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'}, + returns: {arg: 'count', type: 'number'}, + http: {verb: 'get', path: '/count'} }); - var updateAttributesOptions = { - aliases: ['updateAttributes'], - description: 'Patch attributes for a model instance and persist it into the data source.', + setRemoting(PersistedModel.prototype, 'updateAttributes', { + description: 'Update attributes for a model instance and persist it into the data source.', accessType: 'WRITE', - accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description: 'An object of model property name/value pairs' }, - returns: { arg: 'data', type: typeName, root: true }, - http: [{ verb: 'patch', path: '/' }], - }; - - setRemoting(PersistedModel.prototype, 'patchAttributes', updateAttributesOptions); - - if (!options.replaceOnPUT) { - updateAttributesOptions.http.push({ verb: 'put', path: '/' }); - } + accepts: {arg: 'data', type: 'object', http: {source: 'body'}, description: 'An object of model property name/value pairs'}, + returns: {arg: 'data', type: typeName, root: true}, + http: {verb: 'put', path: '/'} + }); if (options.trackChanges || options.enableRemoteReplication) { setRemoting(PersistedModel, 'diff', { description: 'Get a set of deltas and conflicts since the given checkpoint.', accessType: 'READ', accepts: [ - { arg: 'since', type: 'number', description: 'Find deltas since this checkpoint' }, - { arg: 'remoteChanges', type: 'array', description: 'an array of change objects', - http: { source: 'body' }}, + {arg: 'since', type: 'number', description: 'Find deltas since this checkpoint'}, + {arg: 'remoteChanges', type: 'array', description: 'an array of change objects', + http: {source: 'body'}} ], - returns: { arg: 'result', type: 'object', root: true }, - http: { verb: 'post', path: '/diff' }, + returns: {arg: 'result', type: 'object', root: true}, + http: {verb: 'post', path: '/diff'} }); setRemoting(PersistedModel, 'changes', { description: 'Get the changes to a model since a given checkpoint.' + - 'Provide a filter object to reduce the number of results returned.', + 'Provide a filter object to reduce the number of results returned.', accessType: 'READ', accepts: [ - { arg: 'since', type: 'number', description: - 'Only return changes since this checkpoint' }, - { arg: 'filter', type: 'object', description: - 'Only include changes that match this filter' }, + {arg: 'since', type: 'number', description: 'Only return changes since this checkpoint'}, + {arg: 'filter', type: 'object', description: 'Only include changes that match this filter'} ], - returns: { arg: 'changes', type: 'array', root: true }, - http: { verb: 'get', path: '/changes' }, + returns: {arg: 'changes', type: 'array', root: true}, + http: {verb: 'get', path: '/changes'} }); setRemoting(PersistedModel, 'checkpoint', { @@ -835,15 +738,15 @@ module.exports = function(registry) { // We need to allow this method for users that don't have full // WRITE permissions. accessType: 'REPLICATE', - returns: { arg: 'checkpoint', type: 'object', root: true }, - http: { verb: 'post', path: '/checkpoint' }, + returns: {arg: 'checkpoint', type: 'object', root: true}, + http: {verb: 'post', path: '/checkpoint'} }); setRemoting(PersistedModel, 'currentCheckpoint', { description: 'Get the current checkpoint.', accessType: 'READ', - returns: { arg: 'checkpoint', type: 'object', root: true }, - http: { verb: 'get', path: '/checkpoint' }, + returns: {arg: 'checkpoint', type: 'object', root: true}, + http: {verb: 'get', path: '/checkpoint'} }); setRemoting(PersistedModel, 'createUpdates', { @@ -852,16 +755,16 @@ module.exports = function(registry) { // It is called by the replication algorithm to compile a list // of changes to apply on the target. accessType: 'READ', - accepts: { arg: 'deltas', type: 'array', http: { source: 'body' }}, - returns: { arg: 'updates', type: 'array', root: true }, - http: { verb: 'post', path: '/create-updates' }, + accepts: {arg: 'deltas', type: 'array', http: {source: 'body'}}, + returns: {arg: 'updates', type: 'array', root: true}, + http: {verb: 'post', path: '/create-updates'} }); setRemoting(PersistedModel, 'bulkUpdate', { description: 'Run multiple updates at once. Note: this is not atomic.', accessType: 'WRITE', - accepts: { arg: 'updates', type: 'array' }, - http: { verb: 'post', path: '/bulk-update' }, + accepts: {arg: 'updates', type: 'array'}, + http: {verb: 'post', path: '/bulk-update'} }); setRemoting(PersistedModel, 'findLastChange', { @@ -869,30 +772,47 @@ module.exports = function(registry) { accessType: 'READ', accepts: { arg: 'id', type: 'any', required: true, http: { source: 'path' }, - description: 'Model id', + description: 'Model id' }, returns: { arg: 'result', type: this.Change.modelName, root: true }, - http: { verb: 'get', path: '/:id/changes/last' }, + http: { verb: 'get', path: '/:id/changes/last' } }); setRemoting(PersistedModel, 'updateLastChange', { description: [ 'Update the properties of the most recent change record', - 'kept for this instance.', + 'kept for this instance.' ], accessType: 'WRITE', accepts: [ { arg: 'id', type: 'any', required: true, http: { source: 'path' }, - description: 'Model id', + description: 'Model id' }, { - arg: 'data', type: 'object', http: { source: 'body' }, - description: 'An object of Change property name/value pairs', + arg: 'data', type: 'object', http: {source: 'body'}, + description: 'An object of Change property name/value pairs' }, ], returns: { arg: 'result', type: this.Change.modelName, root: true }, - http: { verb: 'put', path: '/:id/changes/last' }, + http: { verb: 'put', path: '/:id/changes/last' } + }); + } + + if (options.trackChanges) { + // Deprecated (legacy) exports kept for backwards compatibility + // TODO(bajtos) Hide these two exports in LoopBack 3.0 + setRemoting(PersistedModel, 'rectifyAllChanges', { + description: 'Rectify all Model changes.', + accessType: 'WRITE', + http: {verb: 'post', path: '/rectify-all'} + }); + + setRemoting(PersistedModel, 'rectifyChange', { + description: 'Tell loopback that a change to the model with the given id has occurred.', + accessType: 'WRITE', + accepts: {arg: 'id', type: 'any', http: {source: 'path'}}, + http: {verb: 'post', path: '/:id/rectify-change'} }); } @@ -900,18 +820,18 @@ module.exports = function(registry) { description: 'Create a change stream.', accessType: 'READ', http: [ - { verb: 'post', path: '/change-stream' }, - { verb: 'get', path: '/change-stream' }, + {verb: 'post', path: '/change-stream'}, + {verb: 'get', path: '/change-stream'} ], accepts: { arg: 'options', - type: 'object', + type: 'object' }, returns: { arg: 'changes', type: 'ReadableStream', - json: true, - }, + json: true + } }); }; @@ -966,14 +886,14 @@ module.exports = function(registry) { // TODO(ritch) this whole thing could be optimized a bit more Change.find({ where: { checkpoint: { gte: since }, - modelName: this.modelName, + modelName: this.modelName }}, function(err, changes) { if (err) return callback(err); if (!Array.isArray(changes) || changes.length === 0) return callback(null, []); var ids = changes.map(function(change) { return change.getModelId(); }); - filter.where[idName] = { inq: ids }; + filter.where[idName] = {inq: ids}; model.find(filter, function(err, models) { if (err) return callback(err); var modelIds = models.map(function(m) { @@ -1021,10 +941,8 @@ module.exports = function(registry) { * @callback {Function} [callback] Callback function called with `(err, conflicts)` arguments. * @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object). * @param {Conflict[]} conflicts A list of changes that could not be replicated due to conflicts. - * @param {Object} checkpoints The new checkpoints to use as the "since" + * @param {Object] checkpoints The new checkpoints to use as the "since" * argument for the next replication. - * - * @promise */ PersistedModel.replicate = function(since, targetModel, options, callback) { @@ -1100,14 +1018,16 @@ module.exports = function(registry) { 'You must enable change tracking before replicating' ); - var diff, updates, newSourceCp, newTargetCp; + var diff; + var updates; + var newSourceCp, newTargetCp; var tasks = [ checkpoints, getSourceChanges, getDiffFromTarget, createSourceUpdates, - bulkUpdate, + bulkUpdate ]; async.waterfall(tasks, done); @@ -1162,8 +1082,8 @@ module.exports = function(registry) { // filter out updates that were not applied updates = updates.filter(function(u) { return conflicts - .filter(function(d) { return d.modelId === u.change.modelId; }) - .length === 0; + .filter(function(d) { return d.modelId === u.change.modelId; }) + .length === 0; }); return cb(); } @@ -1230,7 +1150,7 @@ module.exports = function(registry) { deltas.forEach(function(change) { change = new Change(change); var type = change.type(); - var update = { type: type, change: change }; + var update = {type: type, change: change}; switch (type) { case Change.CREATE: case Change.UPDATE: @@ -1615,7 +1535,7 @@ module.exports = function(registry) { this.Change = BaseChangeModel.extend(this.modelName + '-change', {}, { - trackModel: this, + trackModel: this } ); @@ -1623,7 +1543,8 @@ module.exports = function(registry) { attachRelatedModels(this); } - // Re-attach related models whenever our datasource is changed. + // We have to attach related model whenever the datasource changes, + // this is a workaround for autoAttach called by loopback.createModel var self = this; this.on('dataSourceAttached', function() { attachRelatedModels(self); @@ -1668,7 +1589,7 @@ module.exports = function(registry) { PersistedModel.findLastChange = function(id, cb) { var Change = this.getChangeModel(); - Change.findOne({ where: { modelId: id }}, cb); + Change.findOne({ where: { modelId: id } }, cb); }; PersistedModel.updateLastChange = function(id, data, cb) { @@ -1704,7 +1625,7 @@ module.exports = function(registry) { var idName = this.getIdName(); var Model = this; - var changes = new PassThrough({ objectMode: true }); + var changes = new PassThrough({objectMode: true}); var writeable = true; changes.destroy = function() { @@ -1754,7 +1675,7 @@ module.exports = function(registry) { var change = { target: target, where: where, - data: data, + data: data }; switch (type) {