-
Notifications
You must be signed in to change notification settings - Fork 367
upsertWithWhere method #1001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
upsertWithWhere method #1001
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -630,7 +630,173 @@ DataAccessObject.upsert = function(data, options, cb) { | |
| } | ||
| return cb.promise; | ||
| }; | ||
| /** | ||
| * 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. | ||
| */ | ||
| DataAccessObject.patchOrCreateWithWhere = | ||
| DataAccessObject.upsertWithWhere = function(where, data, options, cb) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In longer term, we want to promote
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
| var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); | ||
| 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) { | ||
| var err = new Error('data object cannot be empty!'); | ||
| err.statusCode = 400; | ||
| process.nextTick(function() { cb(err); }); | ||
| return cb.promise; | ||
| } | ||
| var hookState = {}; | ||
| var self = this; | ||
| var Model = this; | ||
| var connector = Model.getConnector(); | ||
| var modelName = Model.modelName; | ||
| var query = { where: where }; | ||
| var context = { | ||
| Model: Model, | ||
| query: query, | ||
| hookState: hookState, | ||
| options: options, | ||
| }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please preserve consistency with other existing operation hooks. There is no Ideally, the operation hooks invoked for Take a look at support/describe-operation-hooks.js, it creates a comprehensive table showing what context is provided for different DAO operations. |
||
| Model.notifyObserversOf('access', context, doUpsertWithWhere); | ||
| function doUpsertWithWhere(err, ctx) { | ||
| if (err) return cb(err); | ||
| ctx.data = data; | ||
| if (connector.upsertWithWhere) { | ||
| var context = { | ||
| 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; | ||
| var update = data; | ||
| var inst = data; | ||
| if (!(data instanceof Model)) { | ||
| inst = new Model(data, { applyDefaultValues: false }); | ||
| } | ||
| 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); | ||
|
|
||
| function callConnector() { | ||
| try { | ||
| ctx.where = removeUndefined(ctx.where); | ||
| ctx.where = Model._coerce(ctx.where); | ||
| update = removeUndefined(update); | ||
| update = Model._coerce(update); | ||
| } 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); | ||
| connector.upsertWithWhere(modelName, ctx.where, update, options, done); | ||
| }); | ||
| } | ||
| function done(err, data, info) { | ||
| if (err) return cb(err); | ||
| var contxt = { | ||
| Model: Model, | ||
| data: data, | ||
| isNewInstance: info && info.isNewInstance, | ||
| hookState: ctx.hookState, | ||
| options: options, | ||
| }; | ||
| Model.notifyObserversOf('loaded', contxt, function(err) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ummm not sure why you could not use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are making use of ctx.hookstate , which is defined before we define the context properties for 'loaded'. Using ctx instead will make ctx.hookState undefined. Hence, I have left the variable name as contxt . |
||
| if (err) return cb(err); | ||
| var obj; | ||
| if (contxt.data && !(contxt.data instanceof Model)) { | ||
| inst._initProperties(contxt.data, { persisted: true }); | ||
| obj = inst; | ||
| } else { | ||
| obj = contxt.data; | ||
| } | ||
| var context = { | ||
| Model: Model, | ||
| instance: obj, | ||
| isNewInstance: info ? info.isNewInstance : undefined, | ||
| hookState: hookState, | ||
| options: options, | ||
| }; | ||
| Model.notifyObserversOf('after save', context, function(err) { | ||
| cb(err, obj); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why checking
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On 750 now
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! will be available in the next commit. |
||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| } else { | ||
| var opts = { notify: false }; | ||
| if (ctx.options && ctx.options.transaction) { | ||
| opts.transaction = ctx.options.transaction; | ||
| } | ||
| self.find({ where: ctx.query.where }, opts, function(err, instances) { | ||
| if (err) return cb(err); | ||
| var modelsLength = instances.length; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please return error from
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! will be available in a commit in sometime. |
||
| if (modelsLength === 0) { | ||
| self.create(data, options, cb); | ||
| } else if (modelsLength === 1) { | ||
| var modelInst = instances[0]; | ||
| modelInst.updateAttributes(data, options, cb); | ||
| } else { | ||
| process.nextTick(function() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. Both
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
| var error = new Error('There are multiple instances found.' + | ||
| 'Upsert Operation will not be performed!'); | ||
| error.statusCode = 400; | ||
| cb(error); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make sure all return paths from the top-level methods always return
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done! |
||
| return cb.promise; | ||
| }; | ||
| /** | ||
| * 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -539,6 +539,69 @@ describe('crud-with-options', function() { | |
|
|
||
| }); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No space here please
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its in line with other methods in this file. |
||
| describe('upsertWithWhere', function() { | ||
| beforeEach(seed); | ||
| it('rejects upsertWithWhere (options,cb)', function(done) { | ||
| try { | ||
| User.upsertWithWhere({}, function(err) { | ||
| if (err) return done(err); | ||
| }); | ||
| } catch (ex) { | ||
| ex.message.should.equal('The data argument must be an object'); | ||
| done(); | ||
| } | ||
| }); | ||
|
|
||
| it('rejects upsertWithWhere (cb)', function(done) { | ||
| try { | ||
| User.upsertWithWhere(function(err) { | ||
| if (err) return done(err); | ||
| }); | ||
| } catch (ex) { | ||
| ex.message.should.equal('The where argument must be an object'); | ||
| done(); | ||
| } | ||
| }); | ||
|
|
||
| it('allows upsertWithWhere by accepting where,data and cb as arguments', function(done) { | ||
| User.upsertWithWhere({ name: 'John Lennon' }, { name: 'John Smith' }, function(err) { | ||
| if (err) return done(err); | ||
| User.find({ where: { name: 'John Lennon' }}, function(err, data) { | ||
| if (err) return done(err); | ||
| data.length.should.equal(0); | ||
| User.find({ where: { name: 'John Smith' }}, function(err, data) { | ||
| if (err) return done(err); | ||
| data.length.should.equal(1); | ||
| data[0].name.should.equal('John Smith'); | ||
| data[0].email.should.equal('john@b3atl3s.co.uk'); | ||
| data[0].role.should.equal('lead'); | ||
| data[0].order.should.equal(2); | ||
| data[0].vip.should.equal(true); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('allows upsertWithWhere by accepting where, data, options, and cb as arguments', function(done) { | ||
| options = {}; | ||
| User.upsertWithWhere({ name: 'John Lennon' }, { name: 'John Smith' }, options, function(err) { | ||
| if (err) return done(err); | ||
| User.find({ where: { name: 'John Smith' }}, function(err, data) { | ||
| if (err) return done(err); | ||
| data.length.should.equal(1); | ||
| data[0].name.should.equal('John Smith'); | ||
| data[0].seq.should.equal(0); | ||
| data[0].email.should.equal('john@b3atl3s.co.uk'); | ||
| data[0].role.should.equal('lead'); | ||
| data[0].order.should.equal(2); | ||
| data[0].vip.should.equal(true); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| function seed(done) { | ||
| var beatles = [ | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be else if?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually nevermind, i see the return on line 262 now
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a nit though, we should probably use else if since you use if/else right after anyways