不要怂,就是干,撸起袖子干!

Commit f42d487f by Mick Hansen

Merge pull request #5510 from sushantdhiman/404-flag

rejectOnEmpty mode
2 parents 1ce1a2aa 66ac75f6
# Future # Future
- [ADDED] rejectOnEmpty mode [#272](https://github.com/sequelize/sequelize/issues/272) [#5480](https://github.com/sequelize/sequelize/issues/5480)
- [ADDED] `beforeCount` hook [#5209](https://github.com/sequelize/sequelize/pull/5209) - [ADDED] `beforeCount` hook [#5209](https://github.com/sequelize/sequelize/pull/5209)
- [ADDED] `validationFailed` hook [#1626](https://github.com/sequelize/sequelize/issues/1626) - [ADDED] `validationFailed` hook [#1626](https://github.com/sequelize/sequelize/issues/1626)
- [FIXED] Mark index as `unique: true` when `type: 'UNIQUE'`. Fixes [#5351](https://github.com/sequelize/sequelize/issues/5351) - [FIXED] Mark index as `unique: true` when `type: 'UNIQUE'`. Fixes [#5351](https://github.com/sequelize/sequelize/issues/5351)
......
...@@ -299,3 +299,15 @@ error.InstanceError = function (message) { ...@@ -299,3 +299,15 @@ error.InstanceError = function (message) {
this.message = message; this.message = message;
}; };
util.inherits(error.InstanceError, error.BaseError); util.inherits(error.InstanceError, error.BaseError);
/**
* Thrown when a record was not found, Usually used with rejectOnEmpty mode (see message for details)
* @extends BaseError
* @constructor
*/
error.EmptyResultError = function (message) {
error.BaseError.apply(this, arguments);
this.name = 'SequelizeEmptyResultError';
this.message = message;
};
util.inherits(error.EmptyResultError, error.BaseError);
...@@ -12,6 +12,7 @@ var Utils = require('./utils') ...@@ -12,6 +12,7 @@ var Utils = require('./utils')
, Promise = require('./promise') , Promise = require('./promise')
, QueryTypes = require('./query-types') , QueryTypes = require('./query-types')
, Hooks = require('./hooks') , Hooks = require('./hooks')
, sequelizeErrors = require('./errors')
, _ = require('lodash') , _ = require('lodash')
, associationsMixin = require('./associations/mixin'); , associationsMixin = require('./associations/mixin');
...@@ -34,6 +35,7 @@ var Model = function(name, attributes, options) { ...@@ -34,6 +35,7 @@ var Model = function(name, attributes, options) {
underscored: false, underscored: false,
underscoredAll: false, underscoredAll: false,
paranoid: false, paranoid: false,
rejectOnEmpty: false,
whereCollection: null, whereCollection: null,
schema: null, schema: null,
schemaDelimiter: '', schemaDelimiter: '',
...@@ -1333,6 +1335,7 @@ Model.prototype.all = function(options) { ...@@ -1333,6 +1335,7 @@ Model.prototype.all = function(options) {
* @param {Object} [options.having] * @param {Object} [options.having]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only) * @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL. * @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @param {Boolean|Error Instance} [options.rejectOnEmpty=false] Throws an error when no records found
* *
* @see {Sequelize#query} * @see {Sequelize#query}
* @return {Promise<Array<Instance>>} * @return {Promise<Array<Instance>>}
...@@ -1353,7 +1356,10 @@ Model.prototype.findAll = function(options) { ...@@ -1353,7 +1356,10 @@ Model.prototype.findAll = function(options) {
tableNames[this.getTableName(options)] = true; tableNames[this.getTableName(options)] = true;
options = optClone(options); options = optClone(options);
_.defaults(options, { hooks: true }); _.defaults(options, { hooks: true, rejectOnEmpty: this.options.rejectOnEmpty });
//set rejectOnEmpty option from model config
options.rejectOnEmpty = options.rejectOnEmpty || this.options.rejectOnEmpty;
return Promise.bind(this).then(function() { return Promise.bind(this).then(function() {
conformOptions(options, this); conformOptions(options, this);
...@@ -1404,6 +1410,18 @@ Model.prototype.findAll = function(options) { ...@@ -1404,6 +1410,18 @@ Model.prototype.findAll = function(options) {
return this.runHooks('afterFind', results, options); return this.runHooks('afterFind', results, options);
} }
}).then(function (results) { }).then(function (results) {
//rejectOnEmpty mode
if (_.isEmpty(results) && options.rejectOnEmpty) {
if (typeof options.rejectOnEmpty === 'function') {
throw new options.rejectOnEmpty();
} else if (typeof options.rejectOnEmpty === 'object') {
throw options.rejectOnEmpty;
} else {
throw new sequelizeErrors.EmptyResultError();
}
}
return Model.$findSeparate(results, originalOptions); return Model.$findSeparate(results, originalOptions);
}); });
}; };
...@@ -1516,7 +1534,8 @@ Model.prototype.findOne = function(options) { ...@@ -1516,7 +1534,8 @@ Model.prototype.findOne = function(options) {
// Bypass a possible overloaded findAll. // Bypass a possible overloaded findAll.
return Model.prototype.findAll.call(this, _.defaults(options, { return Model.prototype.findAll.call(this, _.defaults(options, {
plain: true plain: true,
rejectOnEmpty: false
})); }));
}; };
Model.prototype.find = Model.prototype.findOne; Model.prototype.find = Model.prototype.findOne;
......
...@@ -441,6 +441,13 @@ Sequelize.prototype.ConnectionTimedOutError = Sequelize.ConnectionTimedOutError ...@@ -441,6 +441,13 @@ Sequelize.prototype.ConnectionTimedOutError = Sequelize.ConnectionTimedOutError
Sequelize.prototype.InstanceError = Sequelize.InstanceError = Sequelize.prototype.InstanceError = Sequelize.InstanceError =
sequelizeErrors.InstanceError; sequelizeErrors.InstanceError;
/**
* Thrown when a record was not found, Usually used with rejectOnEmpty mode (see message for details)
* @see {Errors#RecordNotFoundError}
*/
Sequelize.prototype.EmptyResultError = Sequelize.EmptyResultError =
sequelizeErrors.EmptyResultError;
Sequelize.prototype.refreshTypes = function () { Sequelize.prototype.refreshTypes = function () {
this.connectionManager.refreshTypeParser(DataTypes); this.connectionManager.refreshTypeParser(DataTypes);
......
...@@ -935,5 +935,65 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -935,5 +935,65 @@ describe(Support.getTestDialectTeaser('Model'), function() {
expect(spy.called).to.be.ok; expect(spy.called).to.be.ok;
}); });
}); });
describe('rejectOnEmpty mode', function() {
it('throws error when record not found by findOne', function() {
return expect(this.User.findOne({
where: {
username: 'ath-kantam-pradakshnami'
},
rejectOnEmpty: true
})).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
});
it('throws error when record not found by findById', function() {
return expect(this.User.findById(4732322332323333232344334354234, {
rejectOnEmpty: true
})).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
});
it('throws error when record not found by find', function() {
return expect(this.User.find({
where: {
username: 'some-username-that-is-not-used-anywhere'
},
rejectOnEmpty: true
})).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
});
it('works from model options', function() {
var Model = current.define('Test', {
username: Sequelize.STRING(100)
},{
rejectOnEmpty: true
});
return Model.sync({ force: true })
.then(function() {
return expect(Model.findOne({
where: {
username: 'some-username-that-is-not-used-anywhere'
}
})).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
});
});
it('resolve null when disabled', function() {
var Model = current.define('Test', {
username: Sequelize.STRING(100)
});
return Model.sync({ force: true })
.then(function() {
return expect(Model.findOne({
where: {
username: 'some-username-that-is-not-used-anywhere-for-sure-this-time'
}
})).to.eventually.be.equal(null);
});
});
});
}); });
}); });
...@@ -1389,4 +1389,61 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -1389,4 +1389,61 @@ describe(Support.getTestDialectTeaser('Model'), function() {
expect(spy.called).to.be.ok; expect(spy.called).to.be.ok;
}); });
}); });
describe('rejectOnEmpty mode', function() {
it('works from model options', function() {
var Model = current.define('Test', {
username: Sequelize.STRING(100)
},{
rejectOnEmpty: true
});
return Model.sync({ force: true })
.then(function() {
return expect(Model.findAll({
where: {
username: 'some-username-that-is-not-used-anywhere'
}
})).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
});
});
it('throws custom error with initialized', function() {
var Model = current.define('Test', {
username: Sequelize.STRING(100)
},{
rejectOnEmpty: new Sequelize.ConnectionError('Some Error') //using custom error instance
});
return Model.sync({ force: true })
.then(function() {
return expect(Model.findAll({
where: {
username: 'some-username-that-is-not-used-anywhere-for-sure-this-time'
}
})).to.eventually.be.rejectedWith(Sequelize.ConnectionError);
});
});
it('throws custom error with instance', function() {
var Model = current.define('Test', {
username: Sequelize.STRING(100)
},{
rejectOnEmpty: Sequelize.ConnectionError //using custom error instance
});
return Model.sync({ force: true })
.then(function() {
return expect(Model.findAll({
where: {
username: 'some-username-that-is-not-used-anywhere-for-sure-this-time'
}
})).to.eventually.be.rejectedWith(Sequelize.ConnectionError);
});
});
});
}); });
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!