2019-05-08 15:45:37 +00:00
|
|
|
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
2016-04-01 22:25:16 +00:00
|
|
|
// Node module: loopback-datasource-juggler
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
2014-05-05 14:23:12 +00:00
|
|
|
|
2016-08-26 13:49:34 +00:00
|
|
|
// Turning on strict for this file breaks lots of test cases;
|
|
|
|
// disabling strict for this file
|
|
|
|
/* eslint-disable strict */
|
|
|
|
|
2014-03-12 23:28:46 +00:00
|
|
|
/*!
|
2013-05-17 15:49:57 +00:00
|
|
|
* Module exports class Model
|
|
|
|
*/
|
|
|
|
module.exports = DataAccessObject;
|
|
|
|
|
2014-03-12 23:28:46 +00:00
|
|
|
/*!
|
2013-05-17 15:49:57 +00:00
|
|
|
* Module dependencies
|
|
|
|
*/
|
2018-12-07 14:54:29 +00:00
|
|
|
const g = require('strong-globalize')();
|
|
|
|
const async = require('async');
|
|
|
|
const jutil = require('./jutil');
|
|
|
|
const DataAccessObjectUtils = require('./model-utils');
|
|
|
|
const ValidationError = require('./validations').ValidationError;
|
|
|
|
const Relation = require('./relations.js');
|
|
|
|
const Inclusion = require('./include.js');
|
|
|
|
const List = require('./list.js');
|
|
|
|
const geo = require('./geo');
|
|
|
|
const Memory = require('./connectors/memory').Memory;
|
|
|
|
const utils = require('./utils');
|
|
|
|
const fieldsToArray = utils.fieldsToArray;
|
|
|
|
const sanitizeQueryOrData = utils.sanitizeQuery;
|
|
|
|
const setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
|
|
|
const idEquals = utils.idEquals;
|
|
|
|
const mergeQuery = utils.mergeQuery;
|
|
|
|
const util = require('util');
|
|
|
|
const assert = require('assert');
|
|
|
|
const BaseModel = require('./model');
|
|
|
|
const debug = require('debug')('loopback:dao');
|
2013-10-11 18:50:00 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-03-12 23:28:46 +00:00
|
|
|
* Base class for all persistent objects.
|
|
|
|
* Provides a common API to access any database connector.
|
2014-05-22 00:50:44 +00:00
|
|
|
* This class describes only abstract behavior. Refer to the specific connector for additional details.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* `DataAccessObject` mixes `Inclusion` classes methods.
|
|
|
|
* @class DataAccessObject
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
|
|
|
function DataAccessObject() {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (DataAccessObject._mixins) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const self = this;
|
|
|
|
const args = arguments;
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject._mixins.forEach(function(m) {
|
2014-01-24 17:09:53 +00:00
|
|
|
m.call(self, args);
|
|
|
|
});
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
|
|
|
|
2013-08-15 06:14:44 +00:00
|
|
|
function idName(m) {
|
2014-09-04 16:32:38 +00:00
|
|
|
return m.definition.idName() || 'id';
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getIdValue(m, data) {
|
2014-09-04 16:32:38 +00:00
|
|
|
return data && data[idName(m)];
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
function copyData(from, to) {
|
2018-12-07 14:54:29 +00:00
|
|
|
for (const key in from) {
|
2016-01-20 23:24:05 +00:00
|
|
|
to[key] = from[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-02 23:27:55 +00:00
|
|
|
function convertSubsetOfPropertiesByType(inst, data) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const typedData = {};
|
|
|
|
for (const key in data) {
|
2016-01-20 23:24:05 +00:00
|
|
|
// Convert the properties by type
|
2016-02-02 23:27:55 +00:00
|
|
|
typedData[key] = inst[key];
|
2016-04-01 13:23:42 +00:00
|
|
|
if (typeof typedData[key] === 'object' &&
|
|
|
|
typedData[key] !== null &&
|
|
|
|
typeof typedData[key].toObject === 'function') {
|
2016-01-20 23:24:05 +00:00
|
|
|
typedData[key] = typedData[key].toObject();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return typedData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply strict check for model's data.
|
|
|
|
* Notice: Please note this method modifies `inst` when `strict` is `validate`.
|
|
|
|
*/
|
|
|
|
function applyStrictCheck(model, strict, data, inst, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const props = model.definition.properties;
|
|
|
|
const keys = Object.keys(data);
|
2018-12-07 15:22:36 +00:00
|
|
|
const result = {};
|
2018-12-07 14:54:29 +00:00
|
|
|
for (let i = 0; i < keys.length; i++) {
|
2018-12-07 15:22:36 +00:00
|
|
|
const key = keys[i];
|
2016-01-20 23:24:05 +00:00
|
|
|
if (props[key]) {
|
|
|
|
result[key] = data[key];
|
2018-08-30 02:57:37 +00:00
|
|
|
} else if (strict && strict !== 'filter') {
|
2016-01-20 23:24:05 +00:00
|
|
|
inst.__unknownProperties.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cb(null, result);
|
|
|
|
}
|
|
|
|
|
2013-08-15 06:14:44 +00:00
|
|
|
function setIdValue(m, data, value) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (data) {
|
|
|
|
data[idName(m)] = value;
|
|
|
|
}
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
function byIdQuery(m, id) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const pk = idName(m);
|
|
|
|
const query = {where: {}};
|
2014-09-06 17:24:30 +00:00
|
|
|
query.where[pk] = id;
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function isWhereByGivenId(Model, where, idValue) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const keys = Object.keys(where);
|
2015-01-21 16:57:47 +00:00
|
|
|
if (keys.length != 1) return false;
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const pk = idName(Model);
|
2015-01-21 16:57:47 +00:00
|
|
|
if (keys[0] !== pk) return false;
|
|
|
|
|
|
|
|
return where[pk] === idValue;
|
|
|
|
}
|
|
|
|
|
2017-04-03 20:00:31 +00:00
|
|
|
function errorModelNotFound(idValue) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const msg = g.f('Could not update attributes. {{Object}} with {{id}} %s does not exist!', idValue);
|
|
|
|
const error = new Error(msg);
|
2017-04-03 20:00:31 +00:00
|
|
|
error.statusCode = error.status = 404;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2017-09-06 05:10:57 +00:00
|
|
|
function invokeConnectorMethod(connector, method, Model, args, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const dataSource = Model.getDataSource();
|
2017-09-06 05:10:57 +00:00
|
|
|
// If the DataSource is a transaction and no transaction object is provide in
|
|
|
|
// the options yet, add it to the options, see: DataSource#transaction()
|
2018-12-07 14:54:29 +00:00
|
|
|
const opts = dataSource.isTransaction && !options.transaction ? Object.assign(
|
2018-07-16 06:46:25 +00:00
|
|
|
options, {transaction: dataSource.currentTransaction}
|
|
|
|
) : options;
|
2018-12-07 14:54:29 +00:00
|
|
|
const optionsSupported = connector[method].length >= args.length + 3;
|
|
|
|
const transaction = opts.transaction;
|
2017-09-06 05:10:57 +00:00
|
|
|
if (transaction) {
|
|
|
|
if (!optionsSupported) {
|
|
|
|
return process.nextTick(function() {
|
|
|
|
cb(new Error(g.f(
|
2018-07-16 06:46:25 +00:00
|
|
|
'The connector does not support {{method}} within a transaction', method
|
|
|
|
)));
|
2017-09-06 05:10:57 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
// transaction isn't always a Transaction instance. Some tests provide a
|
|
|
|
// string to test if options get passed through, so check for ensureActive:
|
|
|
|
if (transaction.ensureActive && !transaction.ensureActive(cb)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelName = Model.modelName;
|
|
|
|
let fullArgs;
|
2017-09-06 05:10:57 +00:00
|
|
|
if (!optionsSupported && method === 'count') {
|
|
|
|
// NOTE: The old count() signature is irregular, with `where` coming last:
|
|
|
|
// [modelName, cb, where]
|
2018-12-07 14:54:29 +00:00
|
|
|
const where = args[0];
|
2017-09-06 05:10:57 +00:00
|
|
|
fullArgs = [modelName, cb, where];
|
|
|
|
} else {
|
|
|
|
// Standard signature: [modelName, ...args, (opts, ) cb]
|
|
|
|
fullArgs = [modelName].concat(args);
|
|
|
|
if (optionsSupported) {
|
|
|
|
fullArgs.push(opts);
|
|
|
|
}
|
|
|
|
fullArgs.push(cb);
|
|
|
|
}
|
|
|
|
connector[method].apply(connector, fullArgs);
|
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject._forDB = function(data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (!(this.getDataSource().isRelational && this.getDataSource().isRelational())) {
|
|
|
|
return data;
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const res = {};
|
|
|
|
for (const propName in data) {
|
|
|
|
const type = this.getPropertyType(propName);
|
2014-01-24 17:09:53 +00:00
|
|
|
if (type === 'JSON' || type === 'Any' || type === 'Object' || data[propName] instanceof Array) {
|
|
|
|
res[propName] = JSON.stringify(data[propName]);
|
|
|
|
} else {
|
|
|
|
res[propName] = data[propName];
|
2013-10-07 04:27:02 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
return res;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.defaultScope = function(target, inst) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let scope = this.definition.settings.scope;
|
2014-09-06 17:44:58 +00:00
|
|
|
if (typeof scope === 'function') {
|
2014-09-07 12:24:06 +00:00
|
|
|
scope = this.definition.settings.scope.call(this, target, inst);
|
2014-09-06 09:13:47 +00:00
|
|
|
}
|
2014-09-06 17:44:58 +00:00
|
|
|
return scope;
|
2014-09-06 09:13:47 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.applyScope = function(query, inst) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const scope = this.defaultScope(query, inst) || {};
|
2014-09-06 12:38:57 +00:00
|
|
|
if (typeof scope === 'object') {
|
2014-12-03 23:07:35 +00:00
|
|
|
mergeQuery(query, scope || {}, this.definition.settings.scope);
|
2014-09-06 12:38:57 +00:00
|
|
|
}
|
2014-09-06 09:13:47 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.applyProperties = function(data, inst) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let properties = this.definition.settings.properties;
|
2014-09-07 12:31:06 +00:00
|
|
|
properties = properties || this.definition.settings.attributes;
|
2014-09-07 12:12:14 +00:00
|
|
|
if (typeof properties === 'object') {
|
|
|
|
util._extend(data, properties);
|
2014-09-07 12:24:06 +00:00
|
|
|
} else if (typeof properties === 'function') {
|
|
|
|
util._extend(data, properties.call(this, data, inst) || {});
|
2014-09-07 12:12:14 +00:00
|
|
|
} else if (properties !== false) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const scope = this.defaultScope(data, inst) || {};
|
2014-09-07 12:12:14 +00:00
|
|
|
if (typeof scope.where === 'object') {
|
|
|
|
setScopeValuesFromWhere(data, scope.where, this);
|
|
|
|
}
|
2014-09-06 17:44:58 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
DataAccessObject.lookupModel = function(data) {
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
/**
|
|
|
|
* Get the connector instance for the given model class
|
|
|
|
* @returns {Connector} The connector instance
|
|
|
|
*/
|
|
|
|
DataAccessObject.getConnector = function() {
|
|
|
|
return this.getDataSource().connector;
|
2016-04-01 11:48:17 +00:00
|
|
|
};
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Create an instance of Model with given data and save to the attached data source. Callback is optional.
|
|
|
|
* Example:
|
|
|
|
*```js
|
|
|
|
* User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
|
|
|
|
* console.log(user instanceof User); // true
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
* Note: You must include a callback and use the created model provided in the callback if your code depends on your model being
|
2014-06-11 19:59:21 +00:00
|
|
|
* saved or having an ID.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [data] Optional data object
|
|
|
|
* @param {Object} [options] Options for create
|
|
|
|
* @param {Function} [cb] Callback function called with these arguments:
|
2013-05-17 15:49:57 +00:00
|
|
|
* - err (null or Error)
|
|
|
|
* - instance (null or Model)
|
|
|
|
*/
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.create = function(data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let Model = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
assert(typeof connector.create === 'function',
|
|
|
|
'create() must be implemented by the connector');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const self = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// create(cb)
|
|
|
|
cb = data;
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// create(data, cb);
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
2017-05-19 23:16:52 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof data === 'object', 'The data argument must be an object or array');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
if (Array.isArray(data)) {
|
2015-01-29 19:52:39 +00:00
|
|
|
// Undefined item will be skipped by async.map() which internally uses
|
|
|
|
// Array.prototype.map(). The following loop makes sure all items are
|
|
|
|
// iterated
|
2018-12-07 14:54:29 +00:00
|
|
|
for (let i = 0, n = data.length; i < n; i++) {
|
2015-01-29 19:52:39 +00:00
|
|
|
if (data[i] === undefined) {
|
|
|
|
data[i] = {};
|
|
|
|
}
|
2014-05-09 22:27:45 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
async.map(data, function(item, done) {
|
2015-02-11 07:57:05 +00:00
|
|
|
self.create(item, options, function(err, result) {
|
2015-01-29 19:52:39 +00:00
|
|
|
// Collect all errors and results
|
2016-08-19 17:46:59 +00:00
|
|
|
done(null, {err: err, result: result || item});
|
2015-01-29 19:52:39 +00:00
|
|
|
});
|
|
|
|
}, function(err, results) {
|
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, results);
|
2015-01-29 19:52:39 +00:00
|
|
|
}
|
|
|
|
// Convert the results into two arrays
|
2018-12-07 14:54:29 +00:00
|
|
|
let errors = null;
|
|
|
|
const data = [];
|
|
|
|
for (let i = 0, n = results.length; i < n; i++) {
|
2015-01-29 19:52:39 +00:00
|
|
|
if (results[i].err) {
|
|
|
|
if (!errors) {
|
|
|
|
errors = [];
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
errors[i] = results[i].err;
|
2014-09-07 12:31:06 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
data[i] = results[i].result;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(errors, data);
|
2015-01-29 19:52:39 +00:00
|
|
|
});
|
2017-05-19 23:16:52 +00:00
|
|
|
return cb.promise;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const enforced = {};
|
|
|
|
let obj;
|
|
|
|
const idValue = getIdValue(this, data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2017-07-24 04:58:35 +00:00
|
|
|
try {
|
2014-01-24 17:09:53 +00:00
|
|
|
// if we come from save
|
2017-07-24 04:58:35 +00:00
|
|
|
if (data instanceof Model && !idValue) {
|
|
|
|
obj = data;
|
|
|
|
} else {
|
|
|
|
obj = new Model(data);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2017-07-24 04:58:35 +00:00
|
|
|
this.applyProperties(enforced, obj);
|
|
|
|
obj.setAttributes(enforced);
|
|
|
|
} catch (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
Model = this.lookupModel(data); // data-specific
|
|
|
|
if (Model !== obj.constructor) obj = new Model(data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-03-19 12:25:03 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-19 12:25:03 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
data = obj.toObject(true);
|
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
// options has precedence on model-setting
|
|
|
|
if (options.validate === false) {
|
|
|
|
return create();
|
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return create();
|
|
|
|
}
|
2015-12-18 14:43:20 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
// validation required
|
2016-04-01 11:48:17 +00:00
|
|
|
obj.isValid(function(valid) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (valid) {
|
|
|
|
create();
|
|
|
|
} else {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(new ValidationError(obj), obj);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
function create() {
|
2016-04-01 11:48:17 +00:00
|
|
|
obj.trigger('create', function(createDone) {
|
|
|
|
obj.trigger('save', function(saveDone) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const _idName = idName(Model);
|
|
|
|
let val = Model._sanitizeData(obj.toObject(true), options);
|
2015-05-13 16:36:29 +00:00
|
|
|
function createCallback(err, id, rev) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (id) {
|
|
|
|
obj.__data[_idName] = id;
|
|
|
|
defineReadonlyProp(obj, _idName, id);
|
|
|
|
}
|
|
|
|
if (rev) {
|
|
|
|
obj._rev = rev;
|
|
|
|
}
|
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, obj);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2014-09-05 15:09:23 +00:00
|
|
|
obj.__persisted = true;
|
2015-05-13 23:14:40 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
data: val,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2015-07-01 21:58:14 +00:00
|
|
|
// By default, the instance passed to create callback is NOT updated
|
|
|
|
// with the changes made through persist/loaded hooks. To preserve
|
|
|
|
// backwards compatibility, we introduced a new setting updateOnLoad,
|
2015-05-21 11:51:30 +00:00
|
|
|
// which if set, will apply these changes to the model instance too.
|
2016-04-01 11:48:17 +00:00
|
|
|
if (Model.settings.updateOnLoad) {
|
2015-05-21 11:51:30 +00:00
|
|
|
obj.setAttributes(context.data);
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
saveDone.call(obj, function() {
|
|
|
|
createDone.call(obj, function() {
|
2015-05-21 11:51:30 +00:00
|
|
|
if (err) {
|
|
|
|
return cb(err, obj);
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
2017-04-05 17:23:26 +00:00
|
|
|
if (options.notify !== false) {
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
cb(null, obj);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
data: val,
|
|
|
|
isNewInstance: true,
|
|
|
|
currentInstance: obj,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
2018-06-26 11:39:00 +00:00
|
|
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
2015-05-13 23:14:40 +00:00
|
|
|
if (err) return cb(err);
|
2018-06-26 11:39:00 +00:00
|
|
|
val = ctx.data;
|
|
|
|
invokeConnectorMethod(connector, 'create', Model, [obj.constructor._forDB(ctx.data)],
|
2017-09-06 05:10:57 +00:00
|
|
|
options, createCallback);
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2015-02-11 07:57:05 +00:00
|
|
|
}, obj, cb);
|
|
|
|
}, obj, cb);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2016-04-29 07:08:15 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2013-07-23 18:16:43 +00:00
|
|
|
function stillConnecting(dataSource, obj, args) {
|
2016-04-01 11:48:17 +00:00
|
|
|
if (typeof args[args.length - 1] === 'function') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return dataSource.ready(obj, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
// promise variant
|
2018-12-07 14:54:29 +00:00
|
|
|
const promiseArgs = Array.prototype.slice.call(args);
|
2016-04-01 11:48:17 +00:00
|
|
|
promiseArgs.callee = args.callee;
|
2018-12-07 14:54:29 +00:00
|
|
|
const cb = utils.createPromiseCallback();
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
promiseArgs.push(cb);
|
|
|
|
if (dataSource.ready(obj, promiseArgs)) {
|
|
|
|
return cb.promise;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2013-07-25 05:51:25 +00:00
|
|
|
}
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Update or insert a model instance: update exiting record if one is found, such that parameter `data.id` matches `id` of model instance;
|
|
|
|
* otherwise, insert a new record.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* NOTE: No setters, validations, or hooks are applied when using upsert.
|
2016-04-02 16:20:51 +00:00
|
|
|
* `updateOrCreate` and `patchOrCreate` are aliases
|
2013-08-09 22:16:32 +00:00
|
|
|
* @param {Object} data The model instance data
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for upsert
|
|
|
|
* @param {Function} cb The callback function (optional).
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2014-07-02 19:20:56 +00:00
|
|
|
// [FIXME] rfeng: This is a hack to set up 'upsert' first so that
|
|
|
|
// 'upsert' will be used as the name for strong-remoting to keep it backward
|
|
|
|
// compatible for angular SDK
|
2016-04-02 16:20:51 +00:00
|
|
|
DataAccessObject.updateOrCreate =
|
|
|
|
DataAccessObject.patchOrCreate =
|
|
|
|
DataAccessObject.upsert = function(data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
2014-05-08 22:46:39 +00:00
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// upsert(cb)
|
|
|
|
cb = data;
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// upsert(data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2016-04-05 04:55:15 +00:00
|
|
|
if (Array.isArray(data)) {
|
|
|
|
cb(new Error('updateOrCreate does not support bulk mode or any array input'));
|
|
|
|
return cb.promise;
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const self = this;
|
|
|
|
let Model = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-02-11 07:57:05 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const id = getIdValue(this, data);
|
2015-03-30 14:52:08 +00:00
|
|
|
if (id === undefined || id === null) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return this.create(data, options, cb);
|
2014-05-08 22:46:39 +00:00
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
let doValidate = undefined;
|
2017-04-03 20:00:31 +00:00
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.validateUpsert === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = Model.settings.validateUpsert;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const forceId = this.settings.forceId;
|
2017-04-03 20:00:31 +00:00
|
|
|
if (forceId) {
|
|
|
|
options = Object.create(options);
|
|
|
|
options.validate = !!doValidate;
|
2017-08-22 20:09:45 +00:00
|
|
|
Model.findById(id, options, function(err, model) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
if (!model) return cb(errorModelNotFound(id));
|
2017-04-03 20:00:31 +00:00
|
|
|
model.updateAttributes(data, options, cb);
|
2017-08-22 20:09:45 +00:00
|
|
|
});
|
2017-05-01 04:24:15 +00:00
|
|
|
return cb.promise;
|
2017-04-03 20:00:31 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-04-01 16:25:04 +00:00
|
|
|
Model: Model,
|
2015-03-30 13:03:45 +00:00
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, doUpdateOrCreate);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
function doUpdateOrCreate(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id);
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.updateOrCreate && isOriginalQuery) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-03-05 14:53:34 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: data,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-05 10:55:04 +00:00
|
|
|
};
|
2015-01-21 16:57:47 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
data = ctx.data;
|
2018-12-07 14:54:29 +00:00
|
|
|
let update = data;
|
|
|
|
let inst = data;
|
2015-06-18 22:19:45 +00:00
|
|
|
if (!(data instanceof Model)) {
|
2016-08-19 17:46:59 +00:00
|
|
|
inst = new Model(data, {applyDefaultValues: false});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
update = inst.toObject(false);
|
|
|
|
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
if (doValidate === false) {
|
2015-05-13 23:14:40 +00:00
|
|
|
callConnector();
|
2015-05-05 06:24:08 +00:00
|
|
|
} else {
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) {
|
2015-11-04 10:05:24 +00:00
|
|
|
if (doValidate) { // backwards compatibility with validateUpsert:undefined
|
2015-05-05 06:24:08 +00:00
|
|
|
return cb(new ValidationError(inst), inst);
|
|
|
|
} else {
|
|
|
|
// TODO(bajtos) Remove validateUpsert:undefined in v3.0
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('Ignoring validation errors in {{updateOrCreate()}}:');
|
|
|
|
g.warn(' %s', new ValidationError(inst).message);
|
2015-05-05 06:24:08 +00:00
|
|
|
// continue with updateOrCreate
|
|
|
|
}
|
|
|
|
}
|
2015-05-13 23:14:40 +00:00
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, update, options);
|
2015-05-13 23:14:40 +00:00
|
|
|
}
|
2015-05-05 06:24:08 +00:00
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
function callConnector() {
|
2018-11-12 21:54:22 +00:00
|
|
|
update = Model._sanitizeData(update, options);
|
2015-05-13 23:14:40 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'updateOrCreate', Model, [update], options, done);
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2015-05-05 06:24:08 +00:00
|
|
|
}
|
2015-04-01 16:25:04 +00:00
|
|
|
function done(err, data, info) {
|
2016-06-01 02:17:23 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
data: data,
|
2015-12-18 15:06:53 +00:00
|
|
|
isNewInstance: info && info.isNewInstance,
|
2015-05-21 11:51:30 +00:00
|
|
|
hookState: ctx.hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let obj;
|
2015-05-21 11:51:30 +00:00
|
|
|
if (data && !(data instanceof Model)) {
|
2016-08-19 17:46:59 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-05-21 11:51:30 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = data;
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err, obj);
|
2015-05-21 11:51:30 +00:00
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
|
2017-04-05 17:23:26 +00:00
|
|
|
if (options.notify !== false) {
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
cb(null, obj);
|
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
}
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const opts = {notify: false};
|
2015-06-23 16:14:36 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-08-19 17:46:59 +00:00
|
|
|
Model.findOne({where: ctx.query.where}, opts, function(err, inst) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
if (!isOriginalQuery) {
|
|
|
|
// The custom query returned from a hook may hide the fact that
|
|
|
|
// there is already a model with `id` value `data[idName(Model)]`
|
|
|
|
delete data[idName(Model)];
|
|
|
|
}
|
|
|
|
if (inst) {
|
2015-02-11 07:57:05 +00:00
|
|
|
inst.updateAttributes(data, options, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
} else {
|
|
|
|
Model = self.lookupModel(data);
|
2018-12-07 14:54:29 +00:00
|
|
|
const obj = new Model(data);
|
2015-02-11 07:57:05 +00:00
|
|
|
obj.save(options, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
2016-08-16 12:36:01 +00:00
|
|
|
/**
|
|
|
|
* Update or insert a model instance based on the search criteria.
|
|
|
|
* If there is a single instance retrieved, update the retrieved model.
|
|
|
|
* Creates a new model if no model instances were found.
|
|
|
|
* Returns an error if multiple instances are found.
|
2016-10-11 16:15:35 +00:00
|
|
|
* @param {Object} [where] `where` filter, like
|
2016-08-16 12:36:01 +00:00
|
|
|
* ```
|
|
|
|
* { key: val, key2: {gt: 'val2'}, ...}
|
|
|
|
* ```
|
|
|
|
* <br/>see
|
|
|
|
* [Where filter](https://docs.strongloop.com/display/LB/Where+filter#Wherefilter-Whereclauseforothermethods).
|
|
|
|
* @param {Object} data The model instance data to insert.
|
|
|
|
* @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 Updated model instance.
|
|
|
|
*/
|
|
|
|
DataAccessObject.patchOrCreateWithWhere =
|
|
|
|
DataAccessObject.upsertWithWhere = function(where, data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
2016-08-16 12:36:01 +00:00
|
|
|
if (connectionPromise) { return connectionPromise; }
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// upsertWithWhere(where, data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
options = options || {};
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
if (Object.keys(data).length === 0) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const err = new Error('data object cannot be empty!');
|
2016-08-16 12:36:01 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
process.nextTick(function() { cb(err); });
|
|
|
|
return cb.promise;
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
|
|
|
const self = this;
|
|
|
|
let Model = this;
|
|
|
|
const connector = Model.getConnector();
|
|
|
|
const query = {where: where};
|
|
|
|
const context = {
|
2016-08-16 12:36:01 +00:00
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('access', context, doUpsertWithWhere);
|
|
|
|
function doUpsertWithWhere(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
ctx.data = data;
|
|
|
|
if (connector.upsertWithWhere) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2016-08-16 12:36:01 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: ctx.data,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = ctx.data;
|
2018-12-07 14:54:29 +00:00
|
|
|
let update = data;
|
|
|
|
let inst = data;
|
2016-08-16 12:36:01 +00:00
|
|
|
if (!(data instanceof Model)) {
|
2016-08-19 17:46:59 +00:00
|
|
|
inst = new Model(data, {applyDefaultValues: false});
|
2016-08-16 12:36:01 +00:00
|
|
|
}
|
|
|
|
update = inst.toObject(false);
|
|
|
|
Model.applyScope(query);
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
if (options.validate === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
callConnector();
|
|
|
|
}, update, options);
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2016-08-16 12:36:01 +00:00
|
|
|
function callConnector() {
|
|
|
|
try {
|
2018-11-09 17:33:05 +00:00
|
|
|
ctx.where = Model._sanitizeQuery(ctx.where, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
ctx.where = Model._coerce(ctx.where, options);
|
2018-11-12 21:54:22 +00:00
|
|
|
update = Model._sanitizeData(update, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
update = Model._coerce(update, options);
|
2016-08-16 12:36:01 +00:00
|
|
|
} catch (err) {
|
|
|
|
return process.nextTick(function() {
|
|
|
|
cb(err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'upsertWithWhere', Model, [ctx.where, update], options, done);
|
2016-08-16 12:36:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
function done(err, data, info) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const contxt = {
|
2016-08-16 12:36:01 +00:00
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: info && info.isNewInstance,
|
|
|
|
hookState: ctx.hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', contxt, function(err) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
let obj;
|
2016-08-16 12:36:01 +00:00
|
|
|
if (contxt.data && !(contxt.data instanceof Model)) {
|
2016-08-19 17:46:59 +00:00
|
|
|
inst._initProperties(contxt.data, {persisted: true});
|
2016-08-16 12:36:01 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = contxt.data;
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-08-16 12:36:01 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const opts = {notify: false};
|
2016-08-16 12:36:01 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-08-19 17:46:59 +00:00
|
|
|
self.find({where: ctx.query.where}, opts, function(err, instances) {
|
2016-08-16 12:36:01 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelsLength = instances.length;
|
2018-06-12 07:13:32 +00:00
|
|
|
if (modelsLength === 0) {
|
2016-08-16 12:36:01 +00:00
|
|
|
self.create(data, options, cb);
|
|
|
|
} else if (modelsLength === 1) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelInst = instances[0];
|
2016-08-16 12:36:01 +00:00
|
|
|
modelInst.updateAttributes(data, options, cb);
|
|
|
|
} else {
|
|
|
|
process.nextTick(function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const error = new Error('There are multiple instances found.' +
|
2016-08-16 12:36:01 +00:00
|
|
|
'Upsert Operation will not be performed!');
|
|
|
|
error.statusCode = 400;
|
|
|
|
cb(error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cb.promise;
|
|
|
|
};
|
2015-12-04 18:49:00 +00:00
|
|
|
/**
|
|
|
|
* Replace or insert a model instance: replace exiting 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
|
|
|
|
* @param {Object} [options] Options for replaceOrCreate
|
|
|
|
* @param {Function} cb The callback function (optional).
|
|
|
|
*/
|
|
|
|
|
|
|
|
DataAccessObject.replaceOrCreate = function replaceOrCreate(data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
2015-12-04 18:49:00 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// replaceOrCreta(data,cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-12-04 18:49:00 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const self = this;
|
|
|
|
let Model = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-12-04 18:49:00 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let id = getIdValue(this, data);
|
2015-12-04 18:49:00 +00:00
|
|
|
if (id === undefined || id === null) {
|
|
|
|
return this.create(data, options, cb);
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const forceId = this.settings.forceId;
|
2017-01-04 16:33:49 +00:00
|
|
|
if (forceId) {
|
|
|
|
return Model.replaceById(id, data, options, cb);
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let inst;
|
2015-12-04 18:49:00 +00:00
|
|
|
if (data instanceof Model) {
|
|
|
|
inst = data;
|
|
|
|
} else {
|
|
|
|
inst = new Model(data);
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const strict = inst.__strict;
|
|
|
|
const context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('access', context, doReplaceOrCreate);
|
|
|
|
|
|
|
|
function doReplaceOrCreate(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id);
|
|
|
|
const where = ctx.query.where;
|
2015-12-04 18:49:00 +00:00
|
|
|
if (connector.replaceOrCreate && isOriginalQuery) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
let update = inst.toObject(false);
|
2015-12-04 18:49:00 +00:00
|
|
|
if (strict) {
|
|
|
|
applyStrictCheck(Model, strict, update, inst, validateAndCallConnector);
|
|
|
|
} else {
|
|
|
|
validateAndCallConnector();
|
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
function validateAndCallConnector(err) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
|
|
|
|
if (options.validate === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, update, options);
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
function callConnector() {
|
2018-11-12 21:54:22 +00:00
|
|
|
update = Model._sanitizeData(update, options);
|
2015-12-04 18:49:00 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'replaceOrCreate', Model, [context.data], options, done);
|
2015-12-04 18:49:00 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
function done(err, data, info) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let obj;
|
2015-12-04 18:49:00 +00:00
|
|
|
if (data && !(data instanceof Model)) {
|
2016-08-19 17:46:59 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-12-04 18:49:00 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = data;
|
|
|
|
}
|
|
|
|
if (err) {
|
|
|
|
cb(err, obj);
|
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj, info);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const opts = {notify: false};
|
2015-12-04 18:49:00 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-08-19 17:46:59 +00:00
|
|
|
Model.findOne({where: ctx.query.where}, opts, function(err, found) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
if (!isOriginalQuery) {
|
|
|
|
// The custom query returned from a hook may hide the fact that
|
|
|
|
// there is already a model with `id` value `data[idName(Model)]`
|
2018-12-07 14:54:29 +00:00
|
|
|
const pkName = idName(Model);
|
2015-12-04 18:49:00 +00:00
|
|
|
delete data[pkName];
|
|
|
|
if (found) id = found[pkName];
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
self.replaceById(id, data, options, cb);
|
|
|
|
} else {
|
|
|
|
Model = self.lookupModel(data);
|
2018-12-07 14:54:29 +00:00
|
|
|
const obj = new Model(data);
|
2015-12-04 18:49:00 +00:00
|
|
|
obj.save(options, cb);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cb.promise;
|
|
|
|
};
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find one record that matches specified query criteria. Same as `find`, but limited to one record, and this function returns an
|
|
|
|
* object, not a collection.
|
|
|
|
* If the specified instance is not found, then create it using data provided as second argument.
|
2014-01-24 17:09:53 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
|
|
|
* For example: `{where: {test: 'me'}}`.
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Object} data Object to create.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Option for findOrCreate
|
2014-12-25 14:19:15 +00:00
|
|
|
* @param {Function} cb Callback called with (err, instance, created)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
assert(arguments.length >= 1, 'At least one argument is required');
|
|
|
|
if (data === undefined && options === undefined && cb === undefined) {
|
|
|
|
assert(typeof query === 'object', 'Single argument must be data object');
|
|
|
|
// findOrCreate(data);
|
|
|
|
// query will be built from data, and method will return Promise
|
|
|
|
data = query;
|
2016-08-19 17:46:59 +00:00
|
|
|
query = {where: data};
|
2018-06-12 07:13:32 +00:00
|
|
|
} else if (options === undefined && cb === undefined) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof data === 'function') {
|
|
|
|
// findOrCreate(data, cb);
|
|
|
|
// query will be built from data
|
|
|
|
cb = data;
|
|
|
|
data = query;
|
2016-08-19 17:46:59 +00:00
|
|
|
query = {where: data};
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findOrCreate(query, data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2016-08-19 17:46:59 +00:00
|
|
|
query = query || {where: {}};
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this;
|
|
|
|
const self = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-02-12 04:49:51 +00:00
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
function _findOrCreate(query, data, currentInstance) {
|
2015-05-13 16:36:29 +00:00
|
|
|
function findOrCreateCallback(err, data, created) {
|
2017-03-20 14:32:09 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: created,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
2018-06-26 11:39:00 +00:00
|
|
|
Model.notifyObserversOf('loaded', context, function(err, ctx) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2018-06-26 11:39:00 +00:00
|
|
|
data = ctx.data;
|
2018-12-07 15:22:36 +00:00
|
|
|
const Model = self.lookupModel(data);
|
|
|
|
let obj;
|
2015-02-12 04:49:51 +00:00
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
if (data) {
|
2019-04-01 11:18:05 +00:00
|
|
|
const ctorOpts = {
|
|
|
|
fields: query.fields,
|
|
|
|
applySetters: false,
|
|
|
|
persisted: true,
|
|
|
|
// see https://github.com/strongloop/loopback-datasource-juggler/issues/1692
|
|
|
|
applyDefaultValues: false,
|
|
|
|
};
|
|
|
|
obj = new Model(data, ctorOpts);
|
2015-05-21 11:51:30 +00:00
|
|
|
}
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
if (created) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [obj, created]);
|
|
|
|
} else {
|
|
|
|
cb(err, obj, created);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [obj, created]);
|
|
|
|
} else {
|
|
|
|
cb(err, obj, created);
|
|
|
|
}
|
2015-02-12 04:49:51 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 21:54:22 +00:00
|
|
|
data = Model._sanitizeData(data, options);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-13 23:14:40 +00:00
|
|
|
Model: Model,
|
|
|
|
where: query.where,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: true,
|
2016-04-01 13:23:42 +00:00
|
|
|
currentInstance: currentInstance,
|
2015-05-13 23:14:40 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'findOrCreate', Model, [query, self._forDB(context.data)],
|
|
|
|
options, findOrCreateCallback);
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2015-02-12 04:49:51 +00:00
|
|
|
}
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.findOrCreate) {
|
2015-02-03 07:34:49 +00:00
|
|
|
query.limit = 1;
|
|
|
|
|
|
|
|
try {
|
2016-06-16 14:34:38 +00:00
|
|
|
this._normalize(query, options);
|
2015-02-03 07:34:49 +00:00
|
|
|
} catch (err) {
|
2016-04-01 11:48:17 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-12 04:49:51 +00:00
|
|
|
cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2015-02-03 07:34:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.applyScope(query);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2016-04-01 11:48:17 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const query = ctx.query;
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const enforced = {};
|
|
|
|
const Model = self.lookupModel(data);
|
|
|
|
const obj = data instanceof Model ? data : new Model(data);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
|
|
|
Model.applyProperties(enforced, obj);
|
|
|
|
obj.setAttributes(enforced);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-19 12:25:03 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const obj = ctx.instance;
|
|
|
|
const data = obj.toObject(true);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
// options has precedence on model-setting
|
|
|
|
if (options.validate === false) {
|
2016-01-06 22:24:07 +00:00
|
|
|
return _findOrCreate(query, data, obj);
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
2016-01-06 22:24:07 +00:00
|
|
|
return _findOrCreate(query, data, obj);
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 07:34:49 +00:00
|
|
|
// validation required
|
2016-04-01 11:48:17 +00:00
|
|
|
obj.isValid(function(valid) {
|
2015-02-03 07:34:49 +00:00
|
|
|
if (valid) {
|
2015-05-13 23:14:40 +00:00
|
|
|
_findOrCreate(query, data, obj);
|
2015-02-03 07:34:49 +00:00
|
|
|
} else {
|
2015-02-12 04:49:51 +00:00
|
|
|
cb(new ValidationError(obj), obj);
|
2015-02-03 07:34:49 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
2014-12-25 14:19:15 +00:00
|
|
|
});
|
2015-02-03 07:34:49 +00:00
|
|
|
} else {
|
2016-04-01 11:48:17 +00:00
|
|
|
Model.findOne(query, options, function(err, record) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (record) {
|
|
|
|
if (cb.promise) {
|
|
|
|
return cb(null, [record, false]);
|
|
|
|
} else {
|
|
|
|
return cb(null, record, false);
|
|
|
|
}
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
Model.create(data, options, function(err, record) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [record, record != null]);
|
|
|
|
} else {
|
|
|
|
cb(err, record, record != null);
|
|
|
|
}
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-08-22 19:24:02 +00:00
|
|
|
* Check whether a model instance exists in database
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {id} id Identifier of object (primary key value)
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, exists: Bool)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.exists = function exists(id, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// exists(id, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
if (id !== undefined && id !== null && id !== '') {
|
2015-02-11 07:57:05 +00:00
|
|
|
this.count(byIdQuery(this, id).where, options, function(err, count) {
|
2014-09-06 17:24:30 +00:00
|
|
|
cb(err, err ? false : count === 1);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
} else {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::exists}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find model instance by ID.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* Example:
|
|
|
|
* ```js
|
|
|
|
* User.findById(23, function(err, user) {
|
|
|
|
* console.info(user.id); // 23
|
|
|
|
* });
|
|
|
|
* ```
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {*} id Primary key value
|
2015-04-16 16:06:53 +00:00
|
|
|
* @param {Object} [filter] The filter that contains `include` or `fields`.
|
|
|
|
* Other settings such as `where`, `order`, `limit`, or `offset` will be
|
|
|
|
* ignored.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-05-13 16:36:29 +00:00
|
|
|
DataAccessObject.findById = function findById(id, filter, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
2015-04-16 16:06:53 +00:00
|
|
|
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof filter === 'function') {
|
2015-02-11 07:57:05 +00:00
|
|
|
// findById(id, cb)
|
2015-04-16 16:06:53 +00:00
|
|
|
cb = filter;
|
|
|
|
filter = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findById(id, query, cb)
|
2015-02-11 07:57:05 +00:00
|
|
|
cb = options;
|
|
|
|
options = {};
|
2015-04-16 16:06:53 +00:00
|
|
|
if (typeof filter === 'object' && !(filter.include || filter.fields)) {
|
|
|
|
// If filter doesn't have include or fields, assuming it's options
|
|
|
|
options = filter;
|
|
|
|
filter = {};
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-16 16:06:53 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
2015-04-16 16:06:53 +00:00
|
|
|
filter = filter || {};
|
2015-02-11 07:57:05 +00:00
|
|
|
|
2015-04-16 16:06:53 +00:00
|
|
|
assert(typeof filter === 'object', 'The filter argument must be an object');
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
|
|
|
return cb.promise;
|
2015-08-12 06:02:29 +00:00
|
|
|
} else if (id == null || id === '') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::findById}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const query = byIdQuery(this, id);
|
2015-04-16 16:06:53 +00:00
|
|
|
if (filter.include) {
|
|
|
|
query.include = filter.include;
|
|
|
|
}
|
|
|
|
if (filter.fields) {
|
|
|
|
query.fields = filter.fields;
|
|
|
|
}
|
|
|
|
this.findOne(query, options, cb);
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
/**
|
|
|
|
* Find model instances by ids
|
|
|
|
* @param {Array} ids An array of ids
|
|
|
|
* @param {Object} query Query filter
|
|
|
|
* @param {Object} [options] Options
|
|
|
|
* @param {Function} cb Callback called with (err, instance)
|
|
|
|
*/
|
|
|
|
DataAccessObject.findByIds = function(ids, query, options, cb) {
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
// findByIds(ids, cb)
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findByIds(ids, query, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-07-29 13:01:47 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
query = query || {};
|
|
|
|
|
|
|
|
assert(Array.isArray(ids), 'The ids argument must be an array');
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
|
|
|
} else if (ids.length === 0) {
|
2016-04-01 11:48:17 +00:00
|
|
|
process.nextTick(function() { cb(null, []); });
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
2014-07-29 13:01:47 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const filter = {where: {}};
|
|
|
|
const pk = idName(this);
|
2016-08-19 17:46:59 +00:00
|
|
|
filter.where[pk] = {inq: [].concat(ids)};
|
2015-02-11 07:57:05 +00:00
|
|
|
mergeQuery(filter, query || {});
|
2015-10-16 16:21:04 +00:00
|
|
|
|
|
|
|
// to know if the result need to be sorted by ids or not
|
|
|
|
// this variable need to be initialized before the call to find, because filter is updated during the call with an order
|
2018-12-07 14:54:29 +00:00
|
|
|
const toSortObjectsByIds = filter.order ? false : true;
|
2015-10-16 16:21:04 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
this.find(filter, options, function(err, results) {
|
2016-04-01 11:48:17 +00:00
|
|
|
cb(err, toSortObjectsByIds ? utils.sortObjectsByIds(pk, ids, results) : results);
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-07-29 13:01:47 +00:00
|
|
|
};
|
|
|
|
|
2013-11-21 15:19:34 +00:00
|
|
|
function convertNullToNotFoundError(ctx, cb) {
|
|
|
|
if (ctx.result !== null) return cb();
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelName = ctx.method.sharedClass.name;
|
|
|
|
const id = ctx.getArgByName('id');
|
|
|
|
const msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
|
|
|
const error = new Error(msg);
|
2013-11-21 15:19:34 +00:00
|
|
|
error.statusCode = error.status = 404;
|
|
|
|
cb(error);
|
|
|
|
}
|
2013-05-24 22:10:34 +00:00
|
|
|
|
2013-07-17 16:05:37 +00:00
|
|
|
// alias function for backwards compat.
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.all = function() {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return DataAccessObject.find.apply(this, arguments);
|
2013-08-22 19:24:02 +00:00
|
|
|
};
|
2013-07-17 16:05:37 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find all instances of Model that match the specified query.
|
|
|
|
* Fields used for filter and sort should be declared with `{index: true}` in model definition.
|
|
|
|
* See [Querying models](http://docs.strongloop.com/display/DOC/Querying+models) for more information.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* For example, find the second page of ten users over age 21 in descending order exluding the password property.
|
2014-03-11 22:38:07 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* ```js
|
|
|
|
* User.find({
|
|
|
|
* where: {
|
|
|
|
* age: {gt: 21}},
|
|
|
|
* order: 'age DESC',
|
|
|
|
* limit: 10,
|
|
|
|
* skip: 10,
|
|
|
|
* fields: {password: false}
|
|
|
|
* },
|
|
|
|
* console.log
|
|
|
|
* );
|
|
|
|
* ```
|
2014-03-11 22:38:07 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @options {Object} [query] Optional JSON object that specifies query criteria and parameters.
|
2014-06-11 19:59:21 +00:00
|
|
|
* @property {Object} where Search criteria in JSON format `{ key: val, key2: {gt: 'val2'}}`.
|
2014-05-22 00:50:44 +00:00
|
|
|
* Operations:
|
|
|
|
* - gt: >
|
|
|
|
* - gte: >=
|
|
|
|
* - lt: <
|
|
|
|
* - lte: <=
|
|
|
|
* - between
|
|
|
|
* - inq: IN
|
|
|
|
* - nin: NOT IN
|
|
|
|
* - neq: !=
|
|
|
|
* - like: LIKE
|
|
|
|
* - nlike: NOT LIKE
|
2016-09-16 12:54:17 +00:00
|
|
|
* - ilike: ILIKE
|
|
|
|
* - nilike: NOT ILIKE
|
2015-07-24 19:56:31 +00:00
|
|
|
* - regexp: REGEXP
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* You can also use `and` and `or` operations. See [Querying models](http://docs.strongloop.com/display/DOC/Querying+models) for more information.
|
|
|
|
* @property {String|Object|Array} include Allows you to load relations of several objects and optimize numbers of requests.
|
|
|
|
* Format examples;
|
|
|
|
* - `'posts'`: Load posts
|
|
|
|
* - `['posts', 'passports']`: Load posts and passports
|
|
|
|
* - `{'owner': 'posts'}`: Load owner and owner's posts
|
|
|
|
* - `{'owner': ['posts', 'passports']}`: Load owner, owner's posts, and owner's passports
|
|
|
|
* - `{'owner': [{posts: 'images'}, 'passports']}`: Load owner, owner's posts, owner's posts' images, and owner's passports
|
|
|
|
* See `DataAccessObject.include()`.
|
|
|
|
* @property {String} order Sort order. Format: `'key1 ASC, key2 DESC'`
|
|
|
|
* @property {Number} limit Maximum number of instances to return.
|
|
|
|
* @property {Number} skip Number of instances to skip.
|
|
|
|
* @property {Number} offset Alias for `skip`.
|
|
|
|
* @property {Object|Array|String} fields Included/excluded fields.
|
|
|
|
* - `['foo']` or `'foo'` - include only the foo property
|
|
|
|
* - `['foo', 'bar']` - include the foo and bar properties. Format:
|
|
|
|
* - `{foo: true}` - include only foo
|
|
|
|
* - `{bat: false}` - include all properties, exclude bat
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2016-05-07 02:37:38 +00:00
|
|
|
* @param {Function} cb Optional callback function. Call this function with two arguments: `err` (null or Error) and an array of instances.
|
|
|
|
* @return {Promise} results If no callback function is provided, a promise (which resolves to an array of instances) is returned
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2013-06-24 19:42:58 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
DataAccessObject.find = function find(query, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
// find(cb);
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// find(query, cb);
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
query = query || {};
|
|
|
|
options = options || {};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
|
|
|
const self = this;
|
|
|
|
const connector = self.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
|
|
|
|
assert(typeof connector.all === 'function',
|
|
|
|
'all() must be implemented by the connector');
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
2016-06-16 14:34:38 +00:00
|
|
|
this._normalize(query, options);
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
2016-04-01 11:48:17 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
|
2014-09-06 12:38:57 +00:00
|
|
|
this.applyScope(query);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2019-05-13 07:36:46 +00:00
|
|
|
let near = query && geo.nearFilter(query.where);
|
2018-12-07 14:54:29 +00:00
|
|
|
const supportsGeo = !!connector.buildNearFilter;
|
2019-02-12 16:58:33 +00:00
|
|
|
let geoQueryObject;
|
2014-01-24 17:09:53 +00:00
|
|
|
|
|
|
|
if (near) {
|
|
|
|
if (supportsGeo) {
|
|
|
|
// convert it
|
2015-05-13 16:36:29 +00:00
|
|
|
connector.buildNearFilter(query, near);
|
2014-03-11 22:38:07 +00:00
|
|
|
} else if (query.where) {
|
2014-01-24 17:09:53 +00:00
|
|
|
// do in memory query
|
|
|
|
// using all documents
|
2014-09-06 17:24:30 +00:00
|
|
|
// TODO [fabien] use default scope here?
|
2016-04-09 14:59:09 +00:00
|
|
|
if (options.notify === false) {
|
|
|
|
queryGeo(query);
|
|
|
|
} else {
|
|
|
|
withNotifyGeo();
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-04-09 14:59:09 +00:00
|
|
|
function withNotifyGeo() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-04-09 14:59:09 +00:00
|
|
|
Model: self,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
self.notifyObserversOf('access', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
queryGeo(ctx.query);
|
|
|
|
});
|
|
|
|
}
|
2019-02-12 16:58:33 +00:00
|
|
|
// already handled
|
|
|
|
return cb.promise;
|
|
|
|
}
|
|
|
|
}
|
2019-05-13 07:13:33 +00:00
|
|
|
|
|
|
|
function geoCallback(err, data) {
|
2019-02-12 16:58:33 +00:00
|
|
|
const memory = new Memory();
|
|
|
|
const modelName = self.modelName;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2019-02-12 16:58:33 +00:00
|
|
|
if (err) {
|
|
|
|
cb(err);
|
|
|
|
} else if (Array.isArray(data)) {
|
|
|
|
memory.define({
|
|
|
|
properties: self.dataSource.definitions[modelName].properties,
|
|
|
|
settings: self.dataSource.definitions[modelName].settings,
|
|
|
|
model: self,
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2019-02-12 16:58:33 +00:00
|
|
|
data.forEach(function(obj) {
|
|
|
|
memory.create(modelName, obj, options, function() {
|
|
|
|
// noop
|
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2019-02-12 16:58:33 +00:00
|
|
|
memory.all(modelName, geoQueryObject, options, allCb);
|
|
|
|
} else {
|
|
|
|
cb(null, []);
|
|
|
|
}
|
|
|
|
}
|
2016-10-24 17:51:02 +00:00
|
|
|
|
2019-02-12 16:58:33 +00:00
|
|
|
function queryGeo(query) {
|
|
|
|
geoQueryObject = query;
|
2019-05-13 07:36:46 +00:00
|
|
|
near = query && geo.nearFilter(query.where);
|
2019-02-12 16:58:33 +00:00
|
|
|
invokeConnectorMethod(connector, 'all', self, [{}], options, geoCallback);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-12-14 17:49:11 +00:00
|
|
|
|
2019-02-12 16:58:33 +00:00
|
|
|
function allCb(err, data) {
|
2015-07-29 17:21:47 +00:00
|
|
|
if (!err && Array.isArray(data)) {
|
2016-03-03 08:32:40 +00:00
|
|
|
async.map(data, function(item, next) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = self.lookupModel(item);
|
2016-04-09 14:59:09 +00:00
|
|
|
if (options.notify === false) {
|
2016-03-03 08:32:40 +00:00
|
|
|
buildResult(item, next);
|
2016-04-09 14:59:09 +00:00
|
|
|
} else {
|
2016-03-03 08:32:40 +00:00
|
|
|
withNotify(item, next);
|
2016-04-09 14:59:09 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2016-03-03 08:32:40 +00:00
|
|
|
function buildResult(data, callback) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ctorOpts = {
|
2015-12-18 14:43:20 +00:00
|
|
|
fields: query.fields,
|
|
|
|
applySetters: false,
|
2016-04-01 11:48:17 +00:00
|
|
|
persisted: true,
|
2019-04-01 11:18:05 +00:00
|
|
|
// see https://github.com/strongloop/loopback-datasource-juggler/issues/1692
|
|
|
|
applyDefaultValues: false,
|
2015-07-21 09:05:55 +00:00
|
|
|
};
|
2018-12-07 14:54:29 +00:00
|
|
|
let obj;
|
2017-03-15 23:28:47 +00:00
|
|
|
try {
|
|
|
|
obj = new Model(data, ctorOpts);
|
|
|
|
} catch (err) {
|
2017-03-16 21:25:29 +00:00
|
|
|
return callback(err);
|
2017-03-15 23:28:47 +00:00
|
|
|
}
|
2015-12-18 14:43:20 +00:00
|
|
|
|
|
|
|
if (query && query.include) {
|
|
|
|
if (query.collect) {
|
|
|
|
// The collect property indicates that the query is to return the
|
|
|
|
// standalone items for a related model, not as child of the parent object
|
|
|
|
// For example, article.tags
|
|
|
|
obj = obj.__cachedRelations[query.collect];
|
|
|
|
if (obj === null) {
|
|
|
|
obj = undefined;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This handles the case to return parent items including the related
|
|
|
|
// models. For example, Article.find({include: 'tags'}, ...);
|
|
|
|
// Try to normalize the include
|
2018-12-07 14:54:29 +00:00
|
|
|
const includes = Inclusion.normalizeInclude(query.include || []);
|
2015-12-18 14:43:20 +00:00
|
|
|
includes.forEach(function(inc) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let relationName = inc;
|
2015-12-18 14:43:20 +00:00
|
|
|
if (utils.isPlainObject(inc)) {
|
|
|
|
relationName = Object.keys(inc)[0];
|
|
|
|
}
|
2015-07-21 09:05:55 +00:00
|
|
|
|
2015-12-18 14:43:20 +00:00
|
|
|
// Promote the included model as a direct property
|
2018-12-07 14:54:29 +00:00
|
|
|
let included = obj.__cachedRelations[relationName];
|
2015-12-18 14:43:20 +00:00
|
|
|
if (Array.isArray(included)) {
|
|
|
|
included = new List(included, null, obj);
|
|
|
|
}
|
|
|
|
if (included) obj.__data[relationName] = included;
|
|
|
|
});
|
|
|
|
delete obj.__data.__cachedRelations;
|
|
|
|
}
|
|
|
|
}
|
2015-07-21 09:05:55 +00:00
|
|
|
|
2016-03-03 08:32:40 +00:00
|
|
|
callback(null, obj);
|
2016-04-09 14:59:09 +00:00
|
|
|
}
|
|
|
|
|
2016-03-03 08:32:40 +00:00
|
|
|
function withNotify(data, callback) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-04-09 14:59:09 +00:00
|
|
|
Model: Model,
|
2016-03-03 08:32:40 +00:00
|
|
|
data: data,
|
2016-04-09 14:59:09 +00:00
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
|
|
|
if (err) return callback(err);
|
2016-03-03 08:32:40 +00:00
|
|
|
buildResult(context.data, callback);
|
2016-04-09 14:59:09 +00:00
|
|
|
});
|
|
|
|
}
|
2015-07-29 17:21:47 +00:00
|
|
|
},
|
2016-03-03 08:32:40 +00:00
|
|
|
function(err, results) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
2014-01-29 01:59:59 +00:00
|
|
|
|
2016-03-03 08:32:40 +00:00
|
|
|
// When applying query.collect, some root items may not have
|
|
|
|
// any related/linked item. We store `undefined` in the results
|
|
|
|
// array in such case, which is not desirable from API consumer's
|
|
|
|
// point of view.
|
|
|
|
results = results.filter(isDefined);
|
|
|
|
|
2015-07-21 09:05:55 +00:00
|
|
|
if (data && data.countBeforeLimit) {
|
|
|
|
results.countBeforeLimit = data.countBeforeLimit;
|
|
|
|
}
|
|
|
|
if (!supportsGeo && near) {
|
|
|
|
results = geo.filter(results, near);
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-07-21 09:05:55 +00:00
|
|
|
cb(err, results);
|
|
|
|
});
|
2016-04-01 13:23:42 +00:00
|
|
|
} else {
|
2015-07-29 17:21:47 +00:00
|
|
|
cb(err, data || []);
|
|
|
|
}
|
2019-02-12 16:58:33 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
if (options.notify === false) {
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'all', self, [query], options, allCb);
|
2015-01-21 16:57:47 +00:00
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: this,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
this.notifyObserversOf('access', context, function(err, ctx) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) return cb(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'all', self, [ctx.query], options, allCb);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2016-03-03 08:32:40 +00:00
|
|
|
function isDefined(value) {
|
|
|
|
return value !== undefined;
|
|
|
|
}
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find one record, same as `find`, but limited to one result. This function returns an object, not a collection.
|
2014-01-24 17:09:53 +00:00
|
|
|
*
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
2014-05-22 00:50:44 +00:00
|
|
|
* For example: `{where: {test: 'me'}}`.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-01-21 16:57:47 +00:00
|
|
|
DataAccessObject.findOne = function findOne(query, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2014-03-11 22:38:07 +00:00
|
|
|
query = query || {};
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2014-03-11 22:38:07 +00:00
|
|
|
query.limit = 1;
|
2016-04-01 11:48:17 +00:00
|
|
|
this.find(query, options, function(err, collection) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (err || !collection || !collection.length > 0) return cb(err, null);
|
|
|
|
cb(err, collection[0]);
|
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Destroy all matching records.
|
|
|
|
* Delete all model instances from data source. Note: destroyAll method does not destroy hooks.
|
|
|
|
* Example:
|
|
|
|
*````js
|
|
|
|
* Product.destroyAll({price: {gt: 99}}, function(err) {
|
|
|
|
// removed matching products
|
|
|
|
* });
|
|
|
|
* ````
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} [where] Optional object that defines the criteria. This is a "where" object. Do NOT pass a filter object.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object) [options] Options
|
2015-03-26 18:01:25 +00:00
|
|
|
* @param {Function} [cb] Callback called with (err, info)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-01 13:23:42 +00:00
|
|
|
DataAccessObject.remove =
|
|
|
|
DataAccessObject.deleteAll =
|
|
|
|
DataAccessObject.destroyAll = function destroyAll(where, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
|
|
|
|
assert(typeof connector.destroyAll === 'function',
|
|
|
|
'destroyAll() must be implemented by the connector');
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof where === 'function') {
|
|
|
|
cb = where;
|
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-09-06 17:24:30 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
where = where || {};
|
|
|
|
options = options || {};
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let query = {where: where};
|
2017-07-03 17:04:37 +00:00
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
if (options.notify === false) {
|
|
|
|
doDelete(where);
|
2014-09-06 17:24:30 +00:00
|
|
|
} else {
|
2016-08-19 17:46:59 +00:00
|
|
|
query = {where: whereIsEmpty(where) ? {} : where};
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2016-04-01 11:48:17 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-04-01 13:23:42 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
2016-04-01 11:48:17 +00:00
|
|
|
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
2016-04-01 13:23:42 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
doDelete(ctx.where);
|
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function doDelete(where) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2017-01-31 15:14:53 +00:00
|
|
|
Model: Model,
|
|
|
|
where: whereIsEmpty(where) ? {} : where,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
if (whereIsEmpty(where)) {
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'destroyAll', Model, [{}], options, done);
|
2015-01-21 16:57:47 +00:00
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
// Support an optional where object
|
2018-10-19 18:56:51 +00:00
|
|
|
// alter configuration of how sanitizeQuery handles undefined values
|
2018-11-09 17:33:05 +00:00
|
|
|
where = Model._sanitizeQuery(where, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
where = Model._coerce(where, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
} catch (err) {
|
|
|
|
return process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'destroyAll', Model, [where], options, done);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 18:01:25 +00:00
|
|
|
function done(err, info) {
|
2015-02-04 16:30:13 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
if (options.notify === false) {
|
2015-03-26 18:01:25 +00:00
|
|
|
return cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2017-04-06 16:02:34 +00:00
|
|
|
info: info,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-26 18:01:25 +00:00
|
|
|
cb(err, info);
|
2014-09-06 17:24:30 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-09-06 17:24:30 +00:00
|
|
|
};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function whereIsEmpty(where) {
|
|
|
|
return !where ||
|
2016-04-01 11:48:17 +00:00
|
|
|
(typeof where === 'object' && Object.keys(where).length === 0);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2013-07-22 16:42:09 +00:00
|
|
|
/**
|
2014-06-11 19:59:21 +00:00
|
|
|
* Delete the record with the specified ID.
|
2014-05-07 18:24:49 +00:00
|
|
|
* Aliases are `destroyById` and `deleteById`.
|
2013-08-09 22:16:32 +00:00
|
|
|
* @param {*} id The id value
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback called with (err)
|
2013-07-22 16:42:09 +00:00
|
|
|
*/
|
2014-05-07 18:24:49 +00:00
|
|
|
|
2014-07-03 06:17:01 +00:00
|
|
|
// [FIXME] rfeng: This is a hack to set up 'deleteById' first so that
|
|
|
|
// 'deleteById' will be used as the name for strong-remoting to keep it backward
|
|
|
|
// compatible for angular SDK
|
2016-04-01 13:23:42 +00:00
|
|
|
DataAccessObject.removeById =
|
|
|
|
DataAccessObject.destroyById =
|
|
|
|
DataAccessObject.deleteById = function deleteById(id, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// destroyById(id, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
2015-02-13 17:34:40 +00:00
|
|
|
|
|
|
|
options = options || {};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-13 17:34:40 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
|
|
|
} else if (id == null || id === '') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::deleteById}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-20 16:49:32 +00:00
|
|
|
this.remove(byIdQuery(this, id).where, options, function(err, info) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const deleted = info && info.count > 0;
|
2015-03-20 16:49:32 +00:00
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
|
|
|
return cb(err);
|
2014-09-06 17:24:30 +00:00
|
|
|
}
|
2015-03-20 16:49:32 +00:00
|
|
|
|
|
|
|
cb(null, info);
|
2014-09-06 17:24:30 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-09-06 17:24:30 +00:00
|
|
|
};
|
2013-07-22 16:42:09 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Return count of matched records. Optional query parameter allows you to count filtered set of model instances.
|
|
|
|
* Example:
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
*```js
|
|
|
|
* User.count({approved: true}, function(err, count) {
|
|
|
|
* console.log(count); // 2081
|
|
|
|
* });
|
|
|
|
* ```
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} [where] Search conditions (optional)
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback, called with (err, count)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.count = function(where, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof where === 'function') {
|
|
|
|
// count(cb)
|
|
|
|
cb = where;
|
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// count(where, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
where = where || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this;
|
|
|
|
const connector = Model.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
assert(typeof connector.count === 'function',
|
|
|
|
'count() must be implemented by the connector');
|
|
|
|
assert(connector.count.length >= 3,
|
|
|
|
'count() must take at least 3 arguments');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const query = {where: where};
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
2018-10-19 18:56:51 +00:00
|
|
|
// alter configuration of how sanitizeQuery handles undefined values
|
2018-11-09 17:33:05 +00:00
|
|
|
where = Model._sanitizeQuery(where, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
where = this._coerce(where, options);
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
2016-04-01 11:48:17 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
2016-08-19 17:46:59 +00:00
|
|
|
query: {where: where},
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
this.notifyObserversOf('access', context, function(err, ctx) {
|
2015-05-13 16:36:29 +00:00
|
|
|
if (err) return cb(err);
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'count', Model, [ctx.query.where], options, cb);
|
2015-05-13 16:36:29 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-07 18:24:49 +00:00
|
|
|
* Save instance. If the instance does not have an ID, call `create` instead.
|
|
|
|
* Triggers: validate, save, update or create.
|
|
|
|
* @options {Object} options Optional options to use.
|
|
|
|
* @property {Boolean} validate Default is true.
|
|
|
|
* @property {Boolean} throws Default is false.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Callback function with err and object arguments
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.prototype.save = function(options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this.constructor;
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
2014-01-24 17:09:53 +00:00
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2014-01-24 17:09:53 +00:00
|
|
|
options = options || {};
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument should be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument should be a function');
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb)) {
|
|
|
|
return cb.promise;
|
2018-06-12 07:13:32 +00:00
|
|
|
} else if (this.isNewRecord()) {
|
2015-05-10 08:44:22 +00:00
|
|
|
return Model.create(this, options, cb);
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (options.validate === undefined) {
|
2015-11-04 10:05:24 +00:00
|
|
|
if (Model.settings.automaticValidation === undefined) {
|
|
|
|
options.validate = true;
|
|
|
|
} else {
|
|
|
|
options.validate = Model.settings.automaticValidation;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-11-04 10:05:24 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options.throws === undefined) {
|
2014-01-24 17:09:53 +00:00
|
|
|
options.throws = false;
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const inst = this;
|
|
|
|
const connector = inst.getConnector();
|
2015-03-11 08:39:16 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-30 10:01:48 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let data = inst.toObject(true);
|
2015-01-30 10:01:48 +00:00
|
|
|
Model.applyProperties(data, inst);
|
|
|
|
inst.setAttributes(data);
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
// validate first
|
|
|
|
if (!options.validate) {
|
|
|
|
return save();
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
inst.isValid(function(valid) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (valid) {
|
|
|
|
save();
|
|
|
|
} else {
|
2018-12-07 14:54:29 +00:00
|
|
|
const err = new ValidationError(inst);
|
2015-01-21 16:57:47 +00:00
|
|
|
// throws option is dangerous for async usage
|
|
|
|
if (options.throws) {
|
|
|
|
throw err;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err, inst);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
// then save
|
|
|
|
function save() {
|
2016-04-01 11:48:17 +00:00
|
|
|
inst.trigger('save', function(saveDone) {
|
|
|
|
inst.trigger('update', function(updateDone) {
|
2018-11-12 21:54:22 +00:00
|
|
|
data = Model._sanitizeData(data, options);
|
2015-05-13 16:36:29 +00:00
|
|
|
function saveCallback(err, unusedData, result) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, inst);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-19 12:25:03 +00:00
|
|
|
Model: Model,
|
2015-05-21 11:51:30 +00:00
|
|
|
data: data,
|
2015-03-19 12:25:03 +00:00
|
|
|
isNewInstance: result && result.isNewInstance,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-19 12:25:03 +00:00
|
|
|
};
|
2018-06-26 11:39:00 +00:00
|
|
|
Model.notifyObserversOf('loaded', context, function(err, ctx) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2018-06-26 11:39:00 +00:00
|
|
|
data = ctx.data;
|
2016-08-19 17:46:59 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: result && result.isNewInstance,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (err) return cb(err, inst);
|
2016-04-01 11:48:17 +00:00
|
|
|
updateDone.call(inst, function() {
|
|
|
|
saveDone.call(inst, function() {
|
2015-05-21 11:51:30 +00:00
|
|
|
cb(err, inst);
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
|
2018-06-26 11:39:00 +00:00
|
|
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
2015-05-13 23:14:40 +00:00
|
|
|
if (err) return cb(err);
|
2018-06-26 11:39:00 +00:00
|
|
|
data = ctx.data;
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'save', Model, [Model._forDB(data)],
|
|
|
|
options, saveCallback);
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2015-02-11 07:57:05 +00:00
|
|
|
}, data, cb);
|
|
|
|
}, data, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-06-17 23:30:02 +00:00
|
|
|
/**
|
|
|
|
* Update multiple instances that match the where clause
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
*```js
|
|
|
|
* Employee.update({managerId: 'x001'}, {managerId: 'x002'}, function(err) {
|
|
|
|
* ...
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param {Object} [where] Search conditions (optional)
|
|
|
|
* @param {Object} data Changes to be made
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for update
|
2015-03-26 18:01:25 +00:00
|
|
|
* @param {Function} cb Callback, called with (err, info)
|
2014-06-17 23:30:02 +00:00
|
|
|
*/
|
|
|
|
DataAccessObject.update =
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.updateAll = function(where, data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-06-17 23:30:02 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
assert(arguments.length >= 1, 'At least one argument is required');
|
|
|
|
|
|
|
|
if (data === undefined && options === undefined && cb === undefined && arguments.length === 1) {
|
|
|
|
data = where;
|
|
|
|
where = {};
|
|
|
|
} else if (options === undefined && cb === undefined) {
|
|
|
|
// One of:
|
|
|
|
// updateAll(data, cb)
|
|
|
|
// updateAll(where, data) -> Promise
|
2014-06-17 23:30:02 +00:00
|
|
|
if (typeof data === 'function') {
|
|
|
|
cb = data;
|
|
|
|
data = where;
|
2015-02-11 07:57:05 +00:00
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
// One of:
|
|
|
|
// updateAll(where, data, options) -> Promise
|
|
|
|
// updateAll(where, data, cb)
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
2014-06-17 23:30:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this;
|
|
|
|
const connector = Model.getDataSource().connector;
|
2015-05-13 16:36:29 +00:00
|
|
|
assert(typeof connector.update === 'function',
|
|
|
|
'update() must be implemented by the connector');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const query = {where: where};
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
this.applyProperties(data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let doValidate = false;
|
2017-08-03 11:27:11 +00:00
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.validateUpdate === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = Model.settings.validateUpdate;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
|
|
|
}
|
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
2016-08-19 17:46:59 +00:00
|
|
|
query: {where: where},
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context,
|
2015-01-21 16:57:47 +00:00
|
|
|
function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2017-07-31 17:53:43 +00:00
|
|
|
|
|
|
|
data = ctx.data;
|
2018-12-07 14:54:29 +00:00
|
|
|
let inst = data;
|
2017-07-31 17:53:43 +00:00
|
|
|
if (!(data instanceof Model)) {
|
2017-08-16 21:00:41 +00:00
|
|
|
try {
|
|
|
|
inst = new Model(data, {applyDefaultValues: false});
|
|
|
|
} catch (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2017-07-31 17:53:43 +00:00
|
|
|
}
|
|
|
|
|
2017-08-03 11:27:11 +00:00
|
|
|
if (doValidate === false) {
|
|
|
|
doUpdate(ctx.where, ctx.data);
|
|
|
|
} else {
|
|
|
|
// validation required
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) {
|
|
|
|
return cb(new ValidationError(inst));
|
|
|
|
}
|
2017-07-31 17:53:43 +00:00
|
|
|
doUpdate(ctx.where, ctx.data);
|
2017-08-03 11:27:11 +00:00
|
|
|
}, options);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function doUpdate(where, data) {
|
|
|
|
try {
|
2018-10-19 18:56:51 +00:00
|
|
|
// alter configuration of how sanitizeQuery handles undefined values
|
2018-11-09 17:33:05 +00:00
|
|
|
where = Model._sanitizeQuery(where, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
where = Model._coerce(where, options);
|
2018-11-12 21:54:22 +00:00
|
|
|
data = Model._sanitizeData(data, options);
|
2016-06-16 14:34:38 +00:00
|
|
|
data = Model._coerce(data, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
} catch (err) {
|
2016-04-01 11:48:17 +00:00
|
|
|
return process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
function updateCallback(err, info) {
|
2017-01-06 12:33:54 +00:00
|
|
|
if (err) return cb(err);
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2017-04-06 16:02:34 +00:00
|
|
|
info: info,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err, ctx) {
|
2015-05-13 16:36:29 +00:00
|
|
|
return cb(err, info);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-05-13 23:14:40 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
2017-01-06 12:33:54 +00:00
|
|
|
if (err) return cb(err);
|
2018-06-26 11:39:00 +00:00
|
|
|
data = ctx.data;
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'update', Model, [where, data], options, updateCallback);
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2014-06-17 23:30:02 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-06-17 23:30:02 +00:00
|
|
|
};
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.prototype.isNewRecord = function() {
|
2014-09-05 14:35:01 +00:00
|
|
|
return !this.__persisted;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-07-23 18:16:43 +00:00
|
|
|
* Return connector of current record
|
2013-05-17 15:49:57 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.prototype.getConnector = function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return this.getDataSource().connector;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete object from persistence
|
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* Triggers `destroy` hook (async) before and after destroying object
|
2015-02-11 07:57:05 +00:00
|
|
|
*
|
|
|
|
* @param {Object} [options] Options for delete
|
|
|
|
* @param {Function} cb Callback
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2013-08-18 17:58:53 +00:00
|
|
|
DataAccessObject.prototype.remove =
|
2014-01-24 17:09:53 +00:00
|
|
|
DataAccessObject.prototype.delete =
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.prototype.destroy = function(options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (cb === undefined && typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof options === 'object', 'The options argument should be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument should be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const inst = this;
|
|
|
|
const connector = this.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this.constructor;
|
|
|
|
const id = getIdValue(this.constructor, this);
|
|
|
|
const hookState = {};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2016-04-01 11:48:17 +00:00
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-04-01 13:23:42 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
2016-04-01 11:48:17 +00:00
|
|
|
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
2016-04-01 13:23:42 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
doDeleteInstance(ctx.where);
|
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
});
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function doDeleteInstance(where) {
|
|
|
|
if (!isWhereByGivenId(Model, where, id)) {
|
|
|
|
// A hook modified the query, it is no longer
|
|
|
|
// a simple 'delete model with the given id'.
|
|
|
|
// We must switch to full query-based delete.
|
2016-08-19 17:46:59 +00:00
|
|
|
Model.deleteAll(where, {notify: false}, function(err, info) {
|
2015-03-20 16:49:32 +00:00
|
|
|
if (err) return cb(err, false);
|
2018-12-07 14:54:29 +00:00
|
|
|
const deleted = info && info.count > 0;
|
2015-03-20 16:49:32 +00:00
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
|
|
|
return cb(err, false);
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-05 17:16:12 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
instance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2017-04-06 16:02:34 +00:00
|
|
|
info: info,
|
2015-03-05 17:16:12 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-20 16:49:32 +00:00
|
|
|
cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
inst.trigger('destroy', function(destroyed) {
|
2015-03-20 16:49:32 +00:00
|
|
|
function destroyCallback(err, info) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
const deleted = info && info.count > 0;
|
2015-03-20 16:49:32 +00:00
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
2015-01-21 16:57:47 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
destroyed(function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-03-05 17:16:12 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
instance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2017-04-06 16:02:34 +00:00
|
|
|
info: info,
|
2015-03-05 17:16:12 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-20 16:49:32 +00:00
|
|
|
cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'destroy', Model, [id], options, destroyCallback);
|
2015-01-21 16:57:47 +00:00
|
|
|
}, null, cb);
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-01-24 17:09:53 +00:00
|
|
|
};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
/**
|
|
|
|
* Set a single attribute.
|
|
|
|
* Equivalent to `setAttributes({name: value})`
|
|
|
|
*
|
|
|
|
* @param {String} name Name of property
|
|
|
|
* @param {Mixed} value Value of property
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
|
2014-09-06 09:13:47 +00:00
|
|
|
this[name] = value; // TODO [fabien] - currently not protected by applyProperties
|
2015-01-21 16:57:47 +00:00
|
|
|
};
|
2013-05-24 15:02:58 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-07 18:24:49 +00:00
|
|
|
* Update a single attribute.
|
|
|
|
* Equivalent to `updateAttributes({name: value}, cb)`
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {String} name Name of property
|
|
|
|
* @param {Mixed} value Value of property
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-05-16 17:11:17 +00:00
|
|
|
DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const data = {};
|
2014-01-24 17:09:53 +00:00
|
|
|
data[name] = value;
|
2015-05-16 17:11:17 +00:00
|
|
|
return this.updateAttributes(data, options, cb);
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-07-29 08:54:28 +00:00
|
|
|
* Update set of attributes.
|
|
|
|
*
|
|
|
|
* @trigger `change` hook
|
|
|
|
* @param {Object} data Data to update
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
|
|
|
if (typeof data !== 'object') return;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
this.constructor.applyProperties(data, this);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this.constructor;
|
|
|
|
const inst = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
// update instance's properties
|
2018-12-07 14:54:29 +00:00
|
|
|
for (const key in data) {
|
2014-07-29 08:54:28 +00:00
|
|
|
inst.setAttribute(key, data[key]);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
Model.emit('set', inst);
|
|
|
|
};
|
|
|
|
|
2014-08-19 20:05:27 +00:00
|
|
|
DataAccessObject.prototype.unsetAttribute = function unsetAttribute(name, nullify) {
|
2015-03-27 09:45:14 +00:00
|
|
|
if (nullify || this.constructor.definition.settings.persistUndefinedAsNull) {
|
2014-08-19 20:05:27 +00:00
|
|
|
this[name] = this.__data[name] = null;
|
|
|
|
} else {
|
|
|
|
delete this[name];
|
|
|
|
delete this.__data[name];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
/**
|
|
|
|
* Replace set of attributes.
|
|
|
|
* Performs validation before replacing.
|
|
|
|
*
|
|
|
|
* @trigger `validation`, `save` and `update` hooks
|
|
|
|
* @param {Object} data Data to replace
|
|
|
|
* @param {Object} [options] Options for replace
|
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.replaceAttributes = function(data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const Model = this.constructor;
|
|
|
|
const id = getIdValue(this.constructor, this);
|
2015-12-04 18:49:00 +00:00
|
|
|
return Model.replaceById(id, data, options, cb);
|
|
|
|
};
|
|
|
|
|
|
|
|
DataAccessObject.replaceById = function(id, data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
2015-12-04 18:49:00 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert((typeof data === 'object') && (data !== null),
|
2018-06-12 07:13:32 +00:00
|
|
|
'The data argument must be an object');
|
2015-12-04 18:49:00 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = this.getConnector();
|
2016-08-08 15:39:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let err;
|
2016-08-08 15:39:59 +00:00
|
|
|
if (typeof connector.replaceById !== 'function') {
|
2017-01-31 15:14:53 +00:00
|
|
|
err = new Error(g.f(
|
2016-08-08 15:39:59 +00:00
|
|
|
'The connector %s does not support {{replaceById}} operation. This is not a bug in LoopBack. ' +
|
|
|
|
'Please contact the authors of the connector, preferably via GitHub issues.',
|
2018-07-16 06:46:25 +00:00
|
|
|
connector.name
|
|
|
|
));
|
2016-08-08 15:39:59 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
2015-12-04 18:49:00 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const pkName = idName(this);
|
2015-12-04 18:49:00 +00:00
|
|
|
if (!data[pkName]) data[pkName] = id;
|
2018-12-07 15:22:36 +00:00
|
|
|
let Model = this;
|
|
|
|
let inst;
|
2017-08-16 21:00:41 +00:00
|
|
|
try {
|
2018-12-07 15:22:36 +00:00
|
|
|
inst = new Model(data, {persisted: true});
|
2018-12-07 14:54:29 +00:00
|
|
|
const enforced = {};
|
2017-08-16 21:00:41 +00:00
|
|
|
|
|
|
|
this.applyProperties(enforced, inst);
|
|
|
|
inst.setAttributes(enforced);
|
|
|
|
} catch (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
Model = this.lookupModel(data); // data-specific
|
|
|
|
if (Model !== inst.constructor) inst = new Model(data);
|
2018-12-07 14:54:29 +00:00
|
|
|
const strict = inst.__strict;
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const hookState = {};
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
if (id !== data[pkName]) {
|
2017-01-31 15:14:53 +00:00
|
|
|
err = new Error(g.f('{{id}} property (%s) ' +
|
2016-07-22 19:26:07 +00:00
|
|
|
'cannot be updated from %s to %s', pkName, id, data[pkName]));
|
2015-12-04 18:49:00 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
process.nextTick(function() { cb(err); });
|
|
|
|
return cb.promise;
|
2019-07-25 09:33:59 +00:00
|
|
|
} else {
|
|
|
|
// Ensure any type conversion applied by the instance constructor
|
|
|
|
// on `data.id` is applied on the `id` value too.
|
|
|
|
// Typically, MongoDB converts PK value from a string to an ObjectID.
|
|
|
|
id = inst[pkName];
|
2015-12-04 18:49:00 +00:00
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
2016-04-01 11:48:17 +00:00
|
|
|
|
2016-05-31 22:10:44 +00:00
|
|
|
if (ctx.instance[pkName] !== id && !Model._warned.cannotOverwritePKInBeforeSaveHook) {
|
|
|
|
Model._warned.cannotOverwritePKInBeforeSaveHook = true;
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('WARNING: {{id}} property cannot be changed from %s to %s for model:%s ' +
|
|
|
|
'in {{\'before save\'}} operation hook', id, inst[pkName], Model.modelName);
|
2016-05-31 22:10:44 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
data = inst.toObject(false);
|
|
|
|
|
|
|
|
if (strict) {
|
|
|
|
applyStrictCheck(Model, strict, data, inst, validateAndCallConnector);
|
|
|
|
} else {
|
|
|
|
validateAndCallConnector(null, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateAndCallConnector(err, data) {
|
|
|
|
if (err) return cb(err);
|
2018-11-12 21:54:22 +00:00
|
|
|
data = Model._sanitizeData(data, options);
|
2016-04-01 11:48:17 +00:00
|
|
|
// update instance's properties
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.setAttributes(data);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let doValidate = true;
|
2015-12-04 18:49:00 +00:00
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
if (doValidate) {
|
|
|
|
inst.isValid(function(valid) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
|
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-12-04 18:49:00 +00:00
|
|
|
} else {
|
2016-04-01 11:48:17 +00:00
|
|
|
callConnector();
|
2015-12-04 18:49:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function callConnector() {
|
|
|
|
copyData(data, inst);
|
2018-12-07 14:54:29 +00:00
|
|
|
const typedData = convertSubsetOfPropertiesByType(inst, data);
|
2015-12-04 18:49:00 +00:00
|
|
|
context.data = typedData;
|
2016-04-01 11:48:17 +00:00
|
|
|
|
2018-07-06 16:25:48 +00:00
|
|
|
// Depending on the connector, the database response can
|
|
|
|
// contain information about the updated record(s). This object
|
|
|
|
// has usually database-specific structure and does not match
|
|
|
|
// model properties. For example, MySQL returns OkPacket:
|
|
|
|
// {fieldCount, affectedRows, insertId, /*...*/, changedRows}
|
|
|
|
function replaceCallback(err, dbResponse) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (err) return cb(err);
|
2017-05-17 00:16:45 +00:00
|
|
|
if (typeof connector.generateContextData === 'function') {
|
2018-07-06 16:25:48 +00:00
|
|
|
context = connector.generateContextData(context, dbResponse);
|
2017-05-17 00:16:45 +00:00
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const ctx = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
hookState: hookState,
|
|
|
|
data: context.data,
|
2016-04-01 13:23:42 +00:00
|
|
|
isNewInstance: false,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', ctx, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
2016-05-31 22:10:44 +00:00
|
|
|
if (ctx.data[pkName] !== id && !Model._warned.cannotOverwritePKInLoadedHook) {
|
|
|
|
Model._warned.cannotOverwritePKInLoadedHook = true;
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('WARNING: {{id}} property cannot be changed from %s to %s for model:%s in ' +
|
|
|
|
'{{\'loaded\'}} operation hook',
|
2018-06-12 07:13:32 +00:00
|
|
|
id, ctx.data[pkName], Model.modelName);
|
2016-05-31 22:10:44 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.__persisted = true;
|
2016-05-31 22:10:44 +00:00
|
|
|
ctx.data[pkName] = id;
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.setAttributes(ctx.data);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, inst);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const ctx = {
|
2015-12-04 18:49:00 +00:00
|
|
|
Model: Model,
|
|
|
|
where: byIdQuery(Model, id).where,
|
|
|
|
data: context.data,
|
2016-04-01 13:23:42 +00:00
|
|
|
isNewInstance: false,
|
2015-12-04 18:49:00 +00:00
|
|
|
currentInstance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
2018-01-24 16:37:30 +00:00
|
|
|
if (err) return cb(err);
|
2018-07-06 16:25:48 +00:00
|
|
|
// apply updates made by "persist" observers
|
|
|
|
context.data = ctx.data;
|
2018-06-26 11:39:00 +00:00
|
|
|
invokeConnectorMethod(connector, 'replaceById', Model, [id, Model._forDB(ctx.data)],
|
2017-09-06 05:10:57 +00:00
|
|
|
options, replaceCallback);
|
2016-04-01 11:48:17 +00:00
|
|
|
});
|
2015-12-04 18:49:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return cb.promise;
|
|
|
|
};
|
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
/**
|
|
|
|
* Update set of attributes.
|
2014-05-07 18:24:49 +00:00
|
|
|
* Performs validation before updating.
|
2016-04-02 16:20:51 +00:00
|
|
|
* NOTE: `patchOrCreate` is an alias.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
|
|
|
* @trigger `validation`, `save` and `update` hooks
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Object} data Data to update
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for updateAttributes
|
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-02 16:20:51 +00:00
|
|
|
DataAccessObject.prototype.updateAttributes =
|
|
|
|
DataAccessObject.prototype.patchAttributes =
|
|
|
|
function(data, options, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const self = this;
|
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// updateAttributes(cb)
|
|
|
|
cb = data;
|
|
|
|
data = undefined;
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// updateAttributes(data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert((typeof data === 'object') && (data !== null),
|
|
|
|
'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const inst = this;
|
|
|
|
const Model = this.constructor;
|
|
|
|
const connector = inst.getConnector();
|
2015-05-13 16:36:29 +00:00
|
|
|
assert(typeof connector.updateAttributes === 'function',
|
|
|
|
'updateAttributes() must be implemented by the connector');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const allowExtendedOperators = Model._allowExtendedOperators(options);
|
|
|
|
const strict = this.__strict;
|
|
|
|
const hookState = {};
|
2015-03-11 08:39:16 +00:00
|
|
|
|
2015-01-30 07:26:11 +00:00
|
|
|
// Convert the data to be plain object so that update won't be confused
|
|
|
|
if (data instanceof Model) {
|
|
|
|
data = data.toObject(false);
|
|
|
|
}
|
2018-11-12 21:54:22 +00:00
|
|
|
data = Model._sanitizeData(data, options);
|
2015-01-30 07:26:11 +00:00
|
|
|
|
2015-03-16 16:25:38 +00:00
|
|
|
// Make sure id(s) cannot be changed
|
2018-12-07 14:54:29 +00:00
|
|
|
const idNames = Model.definition.idNames();
|
|
|
|
for (let i = 0, n = idNames.length; i < n; i++) {
|
|
|
|
const idName = idNames[i];
|
2015-03-20 18:06:24 +00:00
|
|
|
if (data[idName] !== undefined && !idEquals(data[idName], inst[idName])) {
|
2018-12-07 15:22:36 +00:00
|
|
|
const err = new Error(g.f('{{id}} cannot be updated from ' +
|
2017-03-16 00:34:09 +00:00
|
|
|
'%s to %s when {{forceId}} is set to true',
|
2018-06-12 07:13:32 +00:00
|
|
|
inst[idName], data[idName]));
|
2015-03-16 16:25:38 +00:00
|
|
|
err.statusCode = 400;
|
2015-06-23 16:08:46 +00:00
|
|
|
process.nextTick(function() {
|
2015-03-16 16:25:38 +00:00
|
|
|
cb(err);
|
|
|
|
});
|
2015-06-23 16:08:46 +00:00
|
|
|
return cb.promise;
|
2015-03-16 16:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let context = {
|
2015-01-21 16:57:47 +00:00
|
|
|
Model: Model,
|
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
2015-03-05 10:55:04 +00:00
|
|
|
data: data,
|
2015-03-05 17:16:12 +00:00
|
|
|
currentInstance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-01-21 16:57:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = ctx.data;
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-07-22 15:09:04 +00:00
|
|
|
if (strict && !allowExtendedOperators) {
|
2016-01-20 23:24:05 +00:00
|
|
|
applyStrictCheck(self.constructor, strict, data, inst, validateAndSave);
|
|
|
|
} else {
|
|
|
|
validateAndSave(null, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateAndSave(err, data) {
|
|
|
|
if (err) return cb(err);
|
2018-12-07 14:54:29 +00:00
|
|
|
let doValidate = true;
|
2016-01-20 23:24:05 +00:00
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
2015-07-29 17:21:47 +00:00
|
|
|
}
|
2016-01-20 23:24:05 +00:00
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
2015-07-21 11:33:42 +00:00
|
|
|
}
|
2015-07-20 09:59:07 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
// update instance's properties
|
2017-03-15 23:28:47 +00:00
|
|
|
try {
|
|
|
|
inst.setAttributes(data);
|
|
|
|
} catch (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
2016-01-20 23:24:05 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
if (doValidate) {
|
|
|
|
inst.isValid(function(valid) {
|
2016-01-20 23:24:05 +00:00
|
|
|
if (!valid) {
|
|
|
|
cb(new ValidationError(inst), inst);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerSave();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2016-01-20 23:24:05 +00:00
|
|
|
} else {
|
2016-04-01 11:48:17 +00:00
|
|
|
triggerSave();
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
function triggerSave() {
|
|
|
|
inst.trigger('save', function(saveDone) {
|
|
|
|
inst.trigger('update', function(done) {
|
2016-01-20 23:24:05 +00:00
|
|
|
copyData(data, inst);
|
2018-12-07 14:54:29 +00:00
|
|
|
const typedData = convertSubsetOfPropertiesByType(inst, data);
|
2018-11-12 21:54:22 +00:00
|
|
|
context.data = Model._sanitizeData(typedData, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2018-07-06 16:25:48 +00:00
|
|
|
// Depending on the connector, the database response can
|
|
|
|
// contain information about the updated record(s), but also
|
|
|
|
// just a number of updated rows (e.g. {count: 1} for MySQL).
|
|
|
|
function updateAttributesCallback(err, dbResponse) {
|
2016-01-20 23:24:05 +00:00
|
|
|
if (err) return cb(err);
|
2017-05-29 19:32:32 +00:00
|
|
|
if (typeof connector.generateContextData === 'function') {
|
2018-07-06 16:25:48 +00:00
|
|
|
context = connector.generateContextData(context, dbResponse);
|
2017-05-29 19:32:32 +00:00
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const ctx = {
|
2016-01-20 23:24:05 +00:00
|
|
|
Model: Model,
|
|
|
|
data: context.data,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2016-09-17 00:37:24 +00:00
|
|
|
isNewInstance: false,
|
2016-01-20 23:24:05 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', ctx, function(err) {
|
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
inst.__persisted = true;
|
2015-11-04 10:05:24 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
// By default, the instance passed to updateAttributes callback is NOT updated
|
|
|
|
// with the changes made through persist/loaded hooks. To preserve
|
|
|
|
// backwards compatibility, we introduced a new setting updateOnLoad,
|
|
|
|
// which if set, will apply these changes to the model instance too.
|
2016-04-01 11:48:17 +00:00
|
|
|
if (Model.settings.updateOnLoad) {
|
2016-01-20 23:24:05 +00:00
|
|
|
inst.setAttributes(ctx.data);
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
done.call(inst, function() {
|
|
|
|
saveDone.call(inst, function() {
|
2016-01-20 23:24:05 +00:00
|
|
|
if (err) return cb(err, inst);
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const context = {
|
2016-01-20 23:24:05 +00:00
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2016-01-20 23:24:05 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, inst);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-08-15 16:01:40 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const ctx = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
2016-01-20 23:24:05 +00:00
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
2015-07-01 10:57:29 +00:00
|
|
|
data: context.data,
|
2016-01-20 23:24:05 +00:00
|
|
|
currentInstance: inst,
|
|
|
|
isNewInstance: false,
|
2015-05-21 11:51:30 +00:00
|
|
|
hookState: hookState,
|
2016-04-01 11:48:17 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
2016-01-20 23:24:05 +00:00
|
|
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
2018-01-24 16:37:30 +00:00
|
|
|
if (err) return cb(err);
|
2018-07-06 16:25:48 +00:00
|
|
|
// apply updates made by "persist" observers
|
|
|
|
context.data = ctx.data;
|
2017-09-06 05:10:57 +00:00
|
|
|
invokeConnectorMethod(connector, 'updateAttributes', Model,
|
2018-06-26 11:39:00 +00:00
|
|
|
[getIdValue(Model, inst), Model._forDB(ctx.data)],
|
2017-09-06 05:10:57 +00:00
|
|
|
options, updateAttributesCallback);
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2016-01-20 23:24:05 +00:00
|
|
|
}, data, cb);
|
2014-07-08 17:54:13 +00:00
|
|
|
}, data, cb);
|
2016-01-20 23:24:05 +00:00
|
|
|
}
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reload object from persistence
|
2014-03-12 23:28:46 +00:00
|
|
|
* Requires `id` member of `object` to be able to call `find`
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Called with (err, instance) arguments
|
2014-06-04 21:02:55 +00:00
|
|
|
* @private
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.prototype.reload = function reload(cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
2014-03-11 22:38:07 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return this.constructor.findById(getIdValue(this.constructor, this), cb);
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2013-05-17 15:49:57 +00:00
|
|
|
* Define readonly property on object
|
|
|
|
*
|
|
|
|
* @param {Object} obj
|
|
|
|
* @param {String} key
|
|
|
|
* @param {Mixed} value
|
2014-06-04 21:02:55 +00:00
|
|
|
* @private
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
|
|
|
function defineReadonlyProp(obj, key, value) {
|
2014-01-24 17:09:53 +00:00
|
|
|
Object.defineProperty(obj, key, {
|
|
|
|
writable: false,
|
|
|
|
enumerable: true,
|
|
|
|
configurable: true,
|
2016-04-01 11:48:17 +00:00
|
|
|
value: value,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
2013-05-28 05:20:30 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const defineScope = require('./scope.js').defineScope;
|
2013-10-02 05:14:21 +00:00
|
|
|
|
2014-03-11 22:38:07 +00:00
|
|
|
/**
|
|
|
|
* Define a scope for the model class. Scopes enable you to specify commonly-used
|
|
|
|
* queries that you can reference as method calls on a model.
|
|
|
|
*
|
|
|
|
* @param {String} name The scope name
|
|
|
|
* @param {Object} query The query object for DataAccessObject.find()
|
|
|
|
* @param {ModelClass} [targetClass] The model class for the query, default to
|
|
|
|
* the declaring model
|
2013-10-02 05:14:21 +00:00
|
|
|
*/
|
2016-04-01 11:48:17 +00:00
|
|
|
DataAccessObject.scope = function(name, query, targetClass, methods, options) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let cls = this;
|
2014-08-08 22:52:30 +00:00
|
|
|
if (options && options.isStatic === false) {
|
|
|
|
cls = cls.prototype;
|
|
|
|
}
|
2015-03-26 14:39:26 +00:00
|
|
|
return defineScope(cls, targetClass || cls, name, query, methods, options);
|
2013-10-02 05:14:21 +00:00
|
|
|
};
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-03-11 22:38:07 +00:00
|
|
|
* Add 'include'
|
|
|
|
*/
|
2013-05-28 05:20:30 +00:00
|
|
|
jutil.mixin(DataAccessObject, Inclusion);
|
2014-03-11 22:38:07 +00:00
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-03-11 22:38:07 +00:00
|
|
|
* Add 'relation'
|
|
|
|
*/
|
2013-06-05 21:33:52 +00:00
|
|
|
jutil.mixin(DataAccessObject, Relation);
|
2015-05-13 16:33:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add 'transaction'
|
|
|
|
*/
|
|
|
|
jutil.mixin(DataAccessObject, require('./transaction'));
|
2015-08-12 06:02:29 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
function PKMissingError(modelName) {
|
|
|
|
this.name = 'PKMissingError';
|
|
|
|
this.message = 'Primary key is missing for the ' + modelName + ' model';
|
|
|
|
}
|
|
|
|
PKMissingError.prototype = new Error();
|
|
|
|
|
|
|
|
function isPKMissing(modelClass, cb) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const hasPK = modelClass.definition.hasPK();
|
2015-08-26 22:23:35 +00:00
|
|
|
if (hasPK) return false;
|
|
|
|
process.nextTick(function() {
|
|
|
|
cb(new PKMissingError(modelClass.modelName));
|
|
|
|
});
|
|
|
|
return true;
|
2015-08-12 06:02:29 +00:00
|
|
|
}
|