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

Commit 060b766c by Sushant

fix(isSoftDeleted): just use deletedAt as flag

1 parent f365f72f
...@@ -9,7 +9,6 @@ const sequelizeErrors = require('./errors'); ...@@ -9,7 +9,6 @@ const sequelizeErrors = require('./errors');
const Dottie = require('dottie'); const Dottie = require('dottie');
const Promise = require('./promise'); const Promise = require('./promise');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment');
const Association = require('./associations/base'); const Association = require('./associations/base');
const HasMany = require('./associations/has-many'); const HasMany = require('./associations/has-many');
const DataTypes = require('./data-types'); const DataTypes = require('./data-types');
...@@ -48,6 +47,112 @@ class Model { ...@@ -48,6 +47,112 @@ class Model {
return this.QueryInterface.QueryGenerator; return this.QueryInterface.QueryGenerator;
} }
/**
* A reference to the sequelize instance
* @see {@link Sequelize}
* @property sequelize
* @return {Sequelize}
*/
get sequelize() {
return this.constructor.sequelize;
}
/**
* Builds a new model instance.
*
* @param {Object} [values={}] an object of key value pairs
* @param {Object} [options]
* @param {Boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
* @param {Boolean} [options.isNewRecord=true]
* @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
*/
constructor(values = {}, options = {}) {
options = _.extend({
isNewRecord: true,
_schema: this.constructor._schema,
_schemaDelimiter: this.constructor._schemaDelimiter
}, options || {});
if (options.attributes) {
options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
}
if (!options.includeValidated) {
this.constructor._conformOptions(options, this.constructor);
if (options.include) {
this.constructor._expandIncludeAll(options);
this.constructor._validateIncludedElements(options);
}
}
this.dataValues = {};
this._previousDataValues = {};
this._changed = {};
this._modelOptions = this.constructor.options;
this._options = options || {};
this.__eagerlyLoadedAssociations = [];
/**
* Returns true if this instance has not yet been persisted to the database
* @property isNewRecord
* @return {Boolean}
*/
this.isNewRecord = options.isNewRecord;
this._initValues(values, options);
}
_initValues(values, options) {
let defaults;
let key;
values = values && _.clone(values) || {};
if (options.isNewRecord) {
defaults = {};
if (this.constructor._hasDefaultValues) {
defaults = _.mapValues(this.constructor._defaultValues, valueFn => {
const value = valueFn();
return value && value instanceof Utils.SequelizeMethod ? value : _.cloneDeep(value);
});
}
// set id to null if not passed as value, a newly created dao has no id
// removing this breaks bulkCreate
// do after default values since it might have UUID as a default value
if (this.constructor.primaryKeyAttribute && !defaults.hasOwnProperty(this.constructor.primaryKeyAttribute)) {
defaults[this.constructor.primaryKeyAttribute] = null;
}
if (this.constructor._timestampAttributes.createdAt && defaults[this.constructor._timestampAttributes.createdAt]) {
this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.createdAt];
}
if (this.constructor._timestampAttributes.updatedAt && defaults[this.constructor._timestampAttributes.updatedAt]) {
this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.updatedAt];
}
if (this.constructor._timestampAttributes.deletedAt && defaults[this.constructor._timestampAttributes.deletedAt]) {
this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.deletedAt];
}
if (Object.keys(defaults).length) {
for (key in defaults) {
if (values[key] === undefined) {
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), defaultsOptions);
delete values[key];
}
}
}
}
this.set(values, options);
}
// validateIncludedElements should have been called before this method // validateIncludedElements should have been called before this method
static _paranoidClause(model, options = {}) { static _paranoidClause(model, options = {}) {
// Apply on each include // Apply on each include
...@@ -3058,118 +3163,6 @@ class Model { ...@@ -3058,118 +3163,6 @@ class Model {
} }
/** /**
* Builds a new model instance.
* @param {Object} [values={}] an object of key value pairs
* @param {Object} [options]
* @param {Boolean} [options.raw=false] If set to true, values will ignore field and virtual setters.
* @param {Boolean} [options.isNewRecord=true]
* @param {Array} [options.include] an array of include options - Used to build prefetched/included model instances. See `set`
*/
constructor(values, options) {
values = values || {};
options = _.extend({
isNewRecord: true,
_schema: this.constructor._schema,
_schemaDelimiter: this.constructor._schemaDelimiter
}, options || {});
if (options.attributes) {
options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
}
if (!options.includeValidated) {
this.constructor._conformOptions(options, this.constructor);
if (options.include) {
this.constructor._expandIncludeAll(options);
this.constructor._validateIncludedElements(options);
}
}
this.dataValues = {};
this._previousDataValues = {};
this._changed = {};
this._modelOptions = this.constructor.options;
this._options = options || {};
this.__eagerlyLoadedAssociations = [];
/**
* Returns true if this instance has not yet been persisted to the database
* @property isNewRecord
* @return {Boolean}
*/
this.isNewRecord = options.isNewRecord;
/**
* Returns the Model the instance was created from.
* @see {@link Model}
* @property Model
* @return {Model}
*/
this._initValues(values, options);
}
_initValues(values, options) {
let defaults;
let key;
values = values && _.clone(values) || {};
if (options.isNewRecord) {
defaults = {};
if (this.constructor._hasDefaultValues) {
defaults = _.mapValues(this.constructor._defaultValues, valueFn => {
const value = valueFn();
return value && value instanceof Utils.SequelizeMethod ? value : _.cloneDeep(value);
});
}
// set id to null if not passed as value, a newly created dao has no id
// removing this breaks bulkCreate
// do after default values since it might have UUID as a default value
if (this.constructor.primaryKeyAttribute && !defaults.hasOwnProperty(this.constructor.primaryKeyAttribute)) {
defaults[this.constructor.primaryKeyAttribute] = null;
}
if (this.constructor._timestampAttributes.createdAt && defaults[this.constructor._timestampAttributes.createdAt]) {
this.dataValues[this.constructor._timestampAttributes.createdAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.createdAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.createdAt];
}
if (this.constructor._timestampAttributes.updatedAt && defaults[this.constructor._timestampAttributes.updatedAt]) {
this.dataValues[this.constructor._timestampAttributes.updatedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.updatedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.updatedAt];
}
if (this.constructor._timestampAttributes.deletedAt && defaults[this.constructor._timestampAttributes.deletedAt]) {
this.dataValues[this.constructor._timestampAttributes.deletedAt] = Utils.toDefaultValue(defaults[this.constructor._timestampAttributes.deletedAt], this.sequelize.options.dialect);
delete defaults[this.constructor._timestampAttributes.deletedAt];
}
if (Object.keys(defaults).length) {
for (key in defaults) {
if (values[key] === undefined) {
this.set(key, Utils.toDefaultValue(defaults[key], this.sequelize.options.dialect), defaultsOptions);
delete values[key];
}
}
}
}
this.set(values, options);
}
/**
* A reference to the sequelize instance
* @see {@link Sequelize}
* @property sequelize
* @return {Sequelize}
*/
get sequelize() {
return this.constructor.sequelize;
}
/**
* Get an object representing the query for this instance, use with `options.where` * Get an object representing the query for this instance, use with `options.where`
* *
* @property where * @property where
...@@ -3877,7 +3870,6 @@ class Model { ...@@ -3877,7 +3870,6 @@ class Model {
* *
* @return {Promise<undefined>} * @return {Promise<undefined>}
*/ */
destroy(options) { destroy(options) {
options = _.extend({ options = _.extend({
hooks: true, hooks: true,
...@@ -3943,21 +3935,9 @@ class Model { ...@@ -3943,21 +3935,9 @@ class Model {
const deletedAt = this.get(this.constructor._timestampAttributes.deletedAt); const deletedAt = this.get(this.constructor._timestampAttributes.deletedAt);
const isSet = deletedAt !== defaultValue; const isSet = deletedAt !== defaultValue;
// No need to check the value of deletedAt if it's equal to the default
// value. If so return the inverse of `isNotSet` since we are asking if
// the model *is* soft-deleted.
if (!isSet) {
return isSet; return isSet;
} }
const now = moment();
const deletedAtIsInTheFuture = moment(deletedAt).isAfter(now);
// If deletedAt is a datetime in the future then the model is *not* soft-deleted.
// Therefore, return the inverse of `deletedAtIsInTheFuture`.
return !deletedAtIsInTheFuture;
}
/** /**
* Restore the row corresponding to this instance. Only available for paranoid models. * Restore the row corresponding to this instance. Only available for paranoid models.
* *
...@@ -4073,7 +4053,6 @@ class Model { ...@@ -4073,7 +4053,6 @@ class Model {
* @return {Boolean} * @return {Boolean}
*/ */
equals(other) { equals(other) {
if (!other || !other.constructor) { if (!other || !other.constructor) {
return false; return false;
} }
......
...@@ -58,20 +58,12 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -58,20 +58,12 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
}).to.throw('Model is not paranoid'); }).to.throw('Model is not paranoid');
}); });
it('should return false if the soft-delete property is the same as ' + it('should return false if the soft-delete property is the same as the default value', function() {
'the default value', function() {
this.paranoidUser.setDataValue('deletedAt', null); this.paranoidUser.setDataValue('deletedAt', null);
expect(this.paranoidUser.isSoftDeleted()).to.be.false; expect(this.paranoidUser.isSoftDeleted()).to.be.false;
}); });
it('should return false if the soft-delete property is set to a date in ' + it('should return true if the soft-delete property is set', function() {
'the future', function() {
this.paranoidUser.setDataValue('deletedAt', moment().add(5, 'days').format());
expect(this.paranoidUser.isSoftDeleted()).to.be.false;
});
it('should return true if the soft-delete property is set to a date ' +
'before now', function() {
this.paranoidUser.setDataValue('deletedAt', moment().subtract(5, 'days').format()); this.paranoidUser.setDataValue('deletedAt', moment().subtract(5, 'days').format());
expect(this.paranoidUser.isSoftDeleted()).to.be.true; expect(this.paranoidUser.isSoftDeleted()).to.be.true;
}); });
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!