I am creating a transparent interface to end point.
For example, a _user_ has his/her first name in another model called _profile_. For convenience, I want my _find_ function can default has filter: {include: 'profiles'}
I consult #443 and have following code
User.on('attached', function() {
var override = User.find;
User.find = function(filter, callback) {
return override(filter, callback);
}
});
And I get this error: TypeError: Object #
Is any clue that I can work on?
You need to call override with the correct receiver:
var override = User.find;
User.find = function(filter, callback) {
return override.apply(this, arguments);
}
Thanks! this solve my problem.
Does that override.apply() go before or after your custom code?
after , since everything after won't be executed.
I have similar issue.
https://github.com/strongloop/loopback/issues/1745
This strategy does not work if I am inside a chain of promises.
If I try this:
var Promise = require("bluebird");
var override = Customer.login;
Customer.login = function (credentials, include, callback) {
var self = this;
return Promise
.cast()
.then(function () {
return override.apply(self, arguments);
});
};
I get a "message": "Cannot read property 'realmRequired' of undefined"
Any suggestion?
You need to implement the pattern followed by these methods internally, by which they support either callback or promise usage:
https://github.com/strongloop/loopback/blob/master/common/models/user.js#L174-L181
You can get the createPromiseCallback method by loading it out of the datasource-juggler project:
var utils = require('loopback-datasource-juggler/lib/utils');
// ...
cb = cb || utils.createPromiseCallback();
Also, @alemhnan the arguments being referenced in then are for the immediate scope, not your custom Customer.login function's scope.
var Promise = require("bluebird");
var override = Customer.login;
Customer.login = function (credentials, include, callback) {
var self = this;
var args = [].slice.call(arguments); // args to pass to the original
return Promise
.then(function () {
return override.apply(self, args);
});
};
Is there a way to execute code _after_ the default find executes? I'm running into the same issue with the following code:
YaIndustryType.on('attached', function() {
var _find = YaIndustryType.find;
YaIndustryType.find = function(filter, callback) {
_find(filter, function(err, result) {
//here's where I need to do stuff
return callback(err, result);
});
};
});
Could someone provide working example of overriding remote methods (e.g. find, create) using Promises? I find the documentation seriously lacking...
@Kitanotori I just met the same issue today. I combined the ideas from the previous discussions.
The following hack works for me with loopback version 3.3.0
ThingBox.on('dataSourceAttached', function(obj) {
var _find = ThingBox.find;
ThingBox.find = function(filter, user, cb) {
_find.call(this, filter, function(err, result) {
console.log('===result', result);
// modify result here (use promise if it's async)
return cb(err, result);
});
};
}
I poured a day into merging the insights of all the people who gave partial answers to this problem. The previous poster neglected dealing with Promisified logic that is assumed when you do not pass in a callback. Here is the code that I ended up with, put inside MyModel.js:
let utils = require('loopback-datasource-juggler/lib/utils');
let decorateFind = function(model) {
let _find = model.find;
model.find = function(filter, user, cb) {
// The next line handles cases where a callback was not supplied so a Promise is expected.
cb = cb || utils.createPromiseCallback();
let results = _find.call(this, filter, user, function(err, myModelInstances){
for (let instance of myModelInstances) {
// DO SOMETHING TO THE INSTANCES HERE!!!
}
cb(err, myModelInstances);
});
return results;
};
};
MyModel.on('dataSourceAttached', function(obj){
decorateFind(MyModel);
});
``` javascript
Category.on("dataSourceAttached", function(obj){
let findById = Category.findById;
Category.findById = function(id,cb){
return new Promise((resolve, reject) => {
findById.call(this, id,function(err,resp){
resolve(resp)
});
});
}
});
This works:
javascript
Model.once('attached', function () {
var _create = Model.create;
Model.create = function (filter, options, cb) {
_create.apply(this).then((data => {
cb(null, data)
})).catch((err => {
cb(err)
}))
}
})
Hello @raymondfeng how I can use a transaction in override method ?
I try two ways but not works:
javascript
Model.once('attached', function () {
var _create = Model.create;
// Override del metodo create
Model.create = function (filter, options, cb) {
// Transaccion
Model.beginTransaction(this,{isolationLevel: Model.Transaction.READ_COMMITTED}).then(function (tx) {
_create.apply(this, { transaction: tx }).then((data => {
cb(null, data)
})).catch((err => {
cb(err)
}))
}).catch(function (err) {
console.log(err);
cb(err);
});
}
})
javascript
Model.once('attached', function () {
var _create = Model.create;
var _beginTransaction = Model.beginTransaction;
// Override del metodo create
Model.create = function (filter, options, cb) {
// Transaccion
_beginTransaction.apply(this, { isolationLevel: Model.Transaction.READ_COMMITTED }).then(function (tx) {
_create.apply(this, { transaction: tx }).then((data => {
cb(null, data)
})).catch((err => {
cb(err)
}))
}).catch(function (err) {
console.log(err);
cb(err);
});
}
})
When it pass at transaciton.js file return undefined for the option 'isolationLevel'

How is my problem ??
Thanks
to override default find function with async await below code will work absolutely fine
CoffeeShop.on('dataSourceAttached', function (obj) {
CoffeeShop.find = async (filter)=> {
try {
let response = await apiService.getAll() // replace this with your logic
return response;
} catch (err) {
console.error(err);
return err;
}
};
I had to make some small changes to @PaulChernoch-Shell's answer to make it work for me in all cases.
const utils = require('loopback-datasource-juggler/lib/utils');
Invoice.on('attached', function() {
const find = Invoice.find;
Invoice.find = function(filter, options, cb) {
cb = cb || utils.createPromiseCallback();
find.call(this, filter, options, (err, invoices) => {
// do something...
cb(err, invoices);
});
return cb.promise;
}
I've made a decorator function also useable with other methods (e.g. findById):
const decorateMethod = (model, methodName, ftn) => {
const originalMethod = model[methodName];
model[methodName] = function() {
const numberOfParameters = originalMethod.length;
// last parameter (if present) is callback
const cb = arguments[numberOfParameters - 1] || utils.createPromiseCallback();
const slicedArgs = Array.prototype.slice.call(arguments, 0, numberOfParameters - 1);
originalMethod.call(this, ...slicedArgs, (err, resp) => {
try {
ftn.call(this, resp);
cb(err, resp);
} catch (e) {
cb(e);
}
});
return cb.promise;
};
};
Usage:
Invoice.on('attached', function() {
decorateMethod(Invoice, 'findById', invoice => {
if (invoice) {
Invoice.mutateLineTotals(invoice.lines);
}
});
});
Most helpful comment
Could someone provide working example of overriding remote methods (e.g. find, create) using Promises? I find the documentation seriously lacking...