Merge pull request #2539 from mountain1234585/upsertWithWhere
Add upsertWithWhere
This commit is contained in:
commit
a6f8ec672d
|
@ -364,6 +364,8 @@ module.exports = function(registry) {
|
||||||
return ACL.WRITE;
|
return ACL.WRITE;
|
||||||
case 'updateOrCreate':
|
case 'updateOrCreate':
|
||||||
return ACL.WRITE;
|
return ACL.WRITE;
|
||||||
|
case 'upsertWithWhere':
|
||||||
|
return ACL.WRITE;
|
||||||
case 'upsert':
|
case 'upsert':
|
||||||
return ACL.WRITE;
|
return ACL.WRITE;
|
||||||
case 'exists':
|
case 'exists':
|
||||||
|
|
|
@ -118,6 +118,28 @@ module.exports = function(registry) {
|
||||||
throwNotAttached(this.modelName, 'upsert');
|
throwNotAttached(this.modelName, 'upsert');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* * @param {Object} [where] `where` filter, like
|
||||||
|
* ```
|
||||||
|
* { 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PersistedModel.upsertWithWhere =
|
||||||
|
PersistedModel.patchOrCreateWithWhere = function upsertWithWhere(where, data, callback) {
|
||||||
|
throwNotAttached(this.modelName, 'upsertWithWhere');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace or insert a model instance; replace existing record if one is found,
|
* Replace or insert a model instance; replace existing record if one is found,
|
||||||
* such that parameter `data.id` matches `id` of model instance; otherwise,
|
* such that parameter `data.id` matches `id` of model instance; otherwise,
|
||||||
|
@ -654,6 +676,21 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'replaceOrCreate', replaceOrCreateOptions);
|
setRemoting(PersistedModel, 'replaceOrCreate', replaceOrCreateOptions);
|
||||||
|
|
||||||
|
setRemoting(PersistedModel, 'upsertWithWhere', {
|
||||||
|
aliases: ['patchOrCreateWithWhere'],
|
||||||
|
description: 'Update an existing model instance or insert a new one into ' +
|
||||||
|
'the data source based on the where criteria.',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{ arg: 'where', type: 'object', http: { source: 'query' },
|
||||||
|
description: 'Criteria to match model instances' },
|
||||||
|
{ arg: 'data', type: 'object', http: { source: 'body' },
|
||||||
|
description: 'An object of model property name/value pairs' },
|
||||||
|
],
|
||||||
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
|
http: { verb: 'post', path: '/upsertWithWhere' },
|
||||||
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'exists', {
|
setRemoting(PersistedModel, 'exists', {
|
||||||
description: 'Check whether a model instance exists in the data source.',
|
description: 'Check whether a model instance exists in the data source.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
|
|
|
@ -122,6 +122,10 @@ describe('access control - integration', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
lt.it.shouldBeDeniedWhenCalledAnonymously('POST', '/api/users/upsertWithWhere');
|
||||||
|
lt.it.shouldBeDeniedWhenCalledUnauthenticated('POST', '/api/users/upsertWithWhere');
|
||||||
|
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'POST', '/api/users/upsertWithWhere');
|
||||||
|
|
||||||
lt.it.shouldBeDeniedWhenCalledAnonymously('DELETE', urlForUser);
|
lt.it.shouldBeDeniedWhenCalledAnonymously('DELETE', urlForUser);
|
||||||
lt.it.shouldBeDeniedWhenCalledUnauthenticated('DELETE', urlForUser);
|
lt.it.shouldBeDeniedWhenCalledUnauthenticated('DELETE', urlForUser);
|
||||||
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForUser);
|
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForUser);
|
||||||
|
@ -193,6 +197,10 @@ describe('access control - integration', function() {
|
||||||
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForBank);
|
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForBank);
|
||||||
lt.it.shouldBeAllowedWhenCalledByUser(SPECIAL_USER, 'DELETE', urlForBank);
|
lt.it.shouldBeAllowedWhenCalledByUser(SPECIAL_USER, 'DELETE', urlForBank);
|
||||||
|
|
||||||
|
lt.it.shouldBeDeniedWhenCalledAnonymously('POST', '/api/banks/upsertWithWhere');
|
||||||
|
lt.it.shouldBeDeniedWhenCalledUnauthenticated('POST', '/api/banks/upsertWithWhere');
|
||||||
|
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'POST', '/api/banks/upsertWithWhere');
|
||||||
|
|
||||||
function urlForBank() {
|
function urlForBank() {
|
||||||
return '/api/banks/' + this.bank.id;
|
return '/api/banks/' + this.bank.id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ describe('DataSource', function() {
|
||||||
assert.isFunc(Color, 'findOne');
|
assert.isFunc(Color, 'findOne');
|
||||||
assert.isFunc(Color, 'create');
|
assert.isFunc(Color, 'create');
|
||||||
assert.isFunc(Color, 'updateOrCreate');
|
assert.isFunc(Color, 'updateOrCreate');
|
||||||
|
assert.isFunc(Color, 'upsertWithWhere');
|
||||||
assert.isFunc(Color, 'upsert');
|
assert.isFunc(Color, 'upsert');
|
||||||
assert.isFunc(Color, 'findOrCreate');
|
assert.isFunc(Color, 'findOrCreate');
|
||||||
assert.isFunc(Color, 'exists');
|
assert.isFunc(Color, 'exists');
|
||||||
|
@ -82,6 +83,7 @@ describe('DataSource', function() {
|
||||||
existsAndShared('_forDB', false);
|
existsAndShared('_forDB', false);
|
||||||
existsAndShared('create', true);
|
existsAndShared('create', true);
|
||||||
existsAndShared('updateOrCreate', true);
|
existsAndShared('updateOrCreate', true);
|
||||||
|
existsAndShared('upsertWithWhere', true);
|
||||||
existsAndShared('upsert', true);
|
existsAndShared('upsert', true);
|
||||||
existsAndShared('findOrCreate', false);
|
existsAndShared('findOrCreate', false);
|
||||||
existsAndShared('exists', true);
|
existsAndShared('exists', true);
|
||||||
|
|
|
@ -146,6 +146,43 @@ describe.onServer('Remote Methods', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Model.upsertWithWhere(where, data, callback)', function() {
|
||||||
|
it('Updates when a Model instance is retreived from data source', function(done) {
|
||||||
|
var taskEmitter = new TaskEmitter();
|
||||||
|
taskEmitter
|
||||||
|
.task(User, 'create', { first: 'jill', second: 'pill' })
|
||||||
|
.task(User, 'create', { first: 'bob', second: 'sob' })
|
||||||
|
.on('done', function() {
|
||||||
|
User.upsertWithWhere({ second: 'pill' }, { second: 'jones' }, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var id = user.id;
|
||||||
|
User.findById(id, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert.equal(user.second, 'jones');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Creates when no Model instance is retreived from data source', function(done) {
|
||||||
|
var taskEmitter = new TaskEmitter();
|
||||||
|
taskEmitter
|
||||||
|
.task(User, 'create', { first: 'simon', second: 'somers' })
|
||||||
|
.on('done', function() {
|
||||||
|
User.upsertWithWhere({ first: 'somers' }, { first: 'Simon' }, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var id = user.id;
|
||||||
|
User.findById(id, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert.equal(user.first, 'Simon');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Example Remote Method', function() {
|
describe('Example Remote Method', function() {
|
||||||
it('Call the method using HTTP / REST', function(done) {
|
it('Call the method using HTTP / REST', function(done) {
|
||||||
request(app)
|
request(app)
|
||||||
|
@ -515,6 +552,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
describe('Model.checkAccessTypeForMethod(remoteMethod)', function() {
|
describe('Model.checkAccessTypeForMethod(remoteMethod)', function() {
|
||||||
shouldReturn('create', ACL.WRITE);
|
shouldReturn('create', ACL.WRITE);
|
||||||
shouldReturn('updateOrCreate', ACL.WRITE);
|
shouldReturn('updateOrCreate', ACL.WRITE);
|
||||||
|
shouldReturn('upsertWithWhere', ACL.WRITE);
|
||||||
shouldReturn('upsert', ACL.WRITE);
|
shouldReturn('upsert', ACL.WRITE);
|
||||||
shouldReturn('exists', ACL.READ);
|
shouldReturn('exists', ACL.READ);
|
||||||
shouldReturn('findById', ACL.READ);
|
shouldReturn('findById', ACL.READ);
|
||||||
|
@ -634,6 +672,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
// 'destroyAll', 'deleteAll', 'remove',
|
// 'destroyAll', 'deleteAll', 'remove',
|
||||||
'create',
|
'create',
|
||||||
'upsert', 'updateOrCreate', 'patchOrCreate',
|
'upsert', 'updateOrCreate', 'patchOrCreate',
|
||||||
|
'upsertWithWhere', 'patchOrCreateWithWhere',
|
||||||
'exists',
|
'exists',
|
||||||
'findById',
|
'findById',
|
||||||
'replaceById',
|
'replaceById',
|
||||||
|
|
|
@ -183,6 +183,15 @@ describe('remoting - integration', function() {
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('has upsertWithWhere remote method', function() {
|
||||||
|
var storeClass = findClass('store');
|
||||||
|
var methods = getFormattedMethodsExcludingRelations(storeClass.methods);
|
||||||
|
var expectedMethods = [
|
||||||
|
'upsertWithWhere(where:object,data:object):store POST /stores/upsertWithWhere',
|
||||||
|
];
|
||||||
|
expect(methods).to.include.members(expectedMethods);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('With model.settings.replaceOnPUT false', function() {
|
describe('With model.settings.replaceOnPUT false', function() {
|
||||||
|
@ -202,6 +211,7 @@ describe('With model.settings.replaceOnPUT false', function() {
|
||||||
'patchOrCreate(data:object):storeWithReplaceOnPUTfalse PUT /stores-updating',
|
'patchOrCreate(data:object):storeWithReplaceOnPUTfalse PUT /stores-updating',
|
||||||
'patchOrCreate(data:object):storeWithReplaceOnPUTfalse PATCH /stores-updating',
|
'patchOrCreate(data:object):storeWithReplaceOnPUTfalse PATCH /stores-updating',
|
||||||
'replaceOrCreate(data:object):storeWithReplaceOnPUTfalse POST /stores-updating/replaceOrCreate',
|
'replaceOrCreate(data:object):storeWithReplaceOnPUTfalse POST /stores-updating/replaceOrCreate',
|
||||||
|
'upsertWithWhere(where:object,data:object):storeWithReplaceOnPUTfalse POST /stores-updating/upsertWithWhere',
|
||||||
'exists(id:any):boolean GET /stores-updating/:id/exists',
|
'exists(id:any):boolean GET /stores-updating/:id/exists',
|
||||||
'exists(id:any):boolean HEAD /stores-updating/:id',
|
'exists(id:any):boolean HEAD /stores-updating/:id',
|
||||||
'findById(id:any,filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/:id',
|
'findById(id:any,filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/:id',
|
||||||
|
|
|
@ -1010,6 +1010,19 @@ describe('Replication / Change APIs', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('detects "upsertWithWhere"', function(done) {
|
||||||
|
givenReplicatedInstance(function(err, inst) {
|
||||||
|
if (err) return done(err);
|
||||||
|
SourceModel.upsertWithWhere(
|
||||||
|
{ name: inst.name },
|
||||||
|
{ name: 'updated' },
|
||||||
|
function(err) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assertChangeRecordedForId(inst.id, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('detects "findOrCreate"', function(done) {
|
it('detects "findOrCreate"', function(done) {
|
||||||
// make sure we bypass find+create and call the connector directly
|
// make sure we bypass find+create and call the connector directly
|
||||||
SourceModel.dataSource.connector.findOrCreate =
|
SourceModel.dataSource.connector.findOrCreate =
|
||||||
|
|
Loading…
Reference in New Issue