Merge pull request #1694 from spurreiter/operation-hook-error

feat: call after operation hook in case of errors
This commit is contained in:
Miroslav Bajtoš 2019-06-01 11:33:53 +02:00 committed by GitHub
commit acb667b73d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 1 deletions

View File

@ -7,6 +7,7 @@
const async = require('async'); const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const debug = require('debug')('loopback:observer');
module.exports = ObserverMixin; module.exports = ObserverMixin;
@ -232,7 +233,24 @@ ObserverMixin.notifyObserversAround = function(operation, context, fn, callback)
function cbForWork(err) { function cbForWork(err) {
const args = [].slice.call(arguments, 0); const args = [].slice.call(arguments, 0);
if (err) return callback.apply(null, args); if (err) {
// call observer in case of error to hook response
context.error = err;
self.notifyObserversOf('after ' + operation + ' error', context,
function(_err, context) {
if (_err && err) {
debug(
'Operation %j failed and "after %s error" hook returned an error too. ' +
'Calling back with the hook error only.' +
'\nOriginal error: %s\nHook error: %s\n',
err.stack || err,
_err.stack || _err
);
}
callback.call(null, _err || err, context);
});
return;
}
// Find the list of params from the callback in addition to err // Find the list of params from the callback in addition to err
const returnedArgs = args.slice(1); const returnedArgs = args.slice(1);
// Set up the array of results // Set up the array of results

View File

@ -325,6 +325,86 @@ describe('async observer', function() {
} }
); );
}); });
it('should call after operation hook on error', function(done) {
const context = {
req: {},
};
const operationError = new Error('The operation failed without result');
let callCount = 0;
function fail(context, done) {
process.nextTick(() => {
done(operationError);
});
}
TestModel.observe('after execute error', function(ctx, next) {
callCount++;
next();
});
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
callCount.should.eql(1);
err.message.should.eql(operationError.message);
ctx.error.message.should.eql(operationError.message);
done();
});
});
it('should call after operation hook on error while overwriting error', function(done) {
const context = {
req: {},
};
const operationError = new Error('The operation failed without result');
const overwriteError = new Error('Overwriting the original error');
let callCount = 0;
function fail(context, done) {
process.nextTick(() => {
done(operationError);
});
}
TestModel.observe('after execute error', function(ctx, next) {
callCount++;
next(overwriteError);
});
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
callCount.should.eql(1);
err.message.should.eql(overwriteError.message);
ctx.error.message.should.eql(operationError.message);
done();
});
});
it('should call after operation hook on error while allowing to change err', function(done) {
const context = {
req: {},
};
const operationError = new Error('The operation failed without result');
let callCount = 0;
function fail(context, done) {
process.nextTick(() => {
done(operationError);
});
}
TestModel.observe('after execute error', function(ctx, next) {
callCount++;
const err = ctx.error;
next(err, ctx);
});
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
callCount.should.eql(1);
err.message.should.eql(operationError.message);
ctx.error.message.should.eql(operationError.message);
done();
});
});
}); });
function pushAndNext(array, value) { function pushAndNext(array, value) {