Skip to content

Commit 01be6ff

Browse files
Add deletedAt feature
1 parent 52cffe1 commit 01be6ff

File tree

5 files changed

+51
-10728
lines changed

5 files changed

+51
-10728
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ index.js
77
index.js.map
88
time-stamp.js
99
time-stamp.js.map
10+
package-lock\.json

README.md

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1-
[![NPM](https://nodei.co/npm/loopback-ds-timestamp-mixin.png?compact=true)](https://nodei.co/npm/loopback-ds-timestamp-mixin/)
2-
3-
[![dependencies](https://img.shields.io/david/clarkbw/loopback-ds-timestamp-mixin.svg)]()
4-
[![devDependencies](https://img.shields.io/david/dev/clarkbw/loopback-ds-timestamp-mixin.svg)]()
5-
[![Build Status](https://travis-ci.org/clarkbw/loopback-ds-timestamp-mixin.svg?branch=master)](https://travis-ci.org/clarkbw/loopback-ds-timestamp-mixin)
6-
[![Coverage Status](https://coveralls.io/repos/clarkbw/loopback-ds-timestamp-mixin/badge.svg)](https://coveralls.io/r/clarkbw/loopback-ds-timestamp-mixin)
7-
81
TIMESTAMPS
92
=============
103

11-
This module is designed for the [Strongloop Loopback](https://github.com/strongloop/loopback) framework. It automatically adds `createdAt` and `updatedAt` attributes to any Model.
4+
This module is designed for the [Strongloop Loopback](https://github.com/strongloop/loopback) framework. It automatically adds `createdAt`, `updatedAt` and `deletedAt` attributes to any Model.
125

136
`createdAt` will be set to the current Date the by using the default property of the attribute.
147

158
`updatedAt` will be set for every update of an object through bulk `updateAll` or instance `model.save` methods.
169

10+
`deletedAt` will be set for every delete of an object through bulk `deleteAll` or instance `model.delete` methods.
11+
1712
This module is implemented with the `before save` [Operation Hook](http://docs.strongloop.com/display/public/LB/Operation+hooks#Operationhooks-beforesave) which requires the loopback-datasource-juggler module greater than [v2.23.0](strongloop/loopback-datasource-juggler@0002aaedeffadda34ae03752d03d0805ab661665).
1813

1914
INSTALL
@@ -23,11 +18,6 @@ INSTALL
2318
npm i loopback-ds-timestamp-mixin --save
2419
```
2520

26-
UPSERT ISSUES
27-
=============
28-
29-
With version 2.33.2 of this module the [upsert validation was turned off](https://github.com/clarkbw/loopback-ds-timestamp-mixin/blob/master/es6/time-stamp.js#L16). This may create issues for your project if upsert validation is required. If you require upsert validation, set the `validateUpsert` option to true, however most upserts will fail unless you supply the `createdAt` and `updatedAt` fields or set `required` option to false.
30-
3121
SERVER CONFIG
3222
=============
3323

@@ -44,7 +34,7 @@ Add the `mixins` property to your `server/model-config.json`:
4434
],
4535
"mixins": [
4636
"loopback/common/mixins",
47-
"../node_modules/loopback-ds-timestamp-mixin",
37+
"../node_modules/loopback-timestamp-mixin",
4838
"../common/mixins"
4939
]
5040
}
@@ -73,16 +63,12 @@ To use with your Models add the `mixins` attribute to the definition object of y
7363
MODEL OPTIONS
7464
=============
7565

76-
The attribute names `createdAt` and `updatedAt` are configurable. To use different values for the default attribute names add the following parameters to the mixin options.
66+
The attribute names `createdAt`, `updatedAt` and `deletedAt` are configurable. To use different values for the default attribute names add the following parameters to the mixin options.
7767

78-
You can also configure whether `createdAt` and `updatedAt` are required or not. This can be useful when applying this mixin to existing data where the `required` constraint would fail by default.
79-
80-
By setting the `validateUpsert` option to true you will prevent this mixin from overriding the default Model settings. With validation turned on most upsert operations will fail with validation errors about missing the required fields like `createdAt` or `updatedAt`.
68+
You can also configure whether `createdAt`, `updatedAt` and `deletedAt` are required or not. This can be useful when applying this mixin to existing data where the `required` constraint would fail by default.
8169

8270
This mixin uses console logs to warn you whenever something might need your attention. If you would prefer not to receive these warnings, you can disable them by setting the option `silenceWarnings` to `true` on a per model basis.
8371

84-
In this example we change `createdAt` and `updatedAt` to `createdOn` and `updatedOn`, respectively. We also change the default `required` to `false` and set `validateUpsert` to true. We also disable console warnings with `silenceWarnings`.
85-
8672
```json
8773
{
8874
"name": "Widget",
@@ -93,10 +79,9 @@ In this example we change `createdAt` and `updatedAt` to `createdOn` and `update
9379
},
9480
"mixins": {
9581
"TimeStamp" : {
96-
"createdAt" : "createdOn",
97-
"updatedAt" : "updatedOn",
98-
"required" : false,
99-
"validateUpsert": true,
82+
"createdAt" : "created_at",
83+
"updatedAt" : "updated_at",
84+
"deletedAt" : "deleted_at",
10085
"silenceWarnings": true
10186
}
10287
}

es6/time-stamp.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default (Model, bootOptions = {}) => {
1010
const options = Object.assign({
1111
createdAt: 'createdAt',
1212
updatedAt: 'updatedAt',
13+
deletedAt: 'deletedAt',
1314
required: true,
1415
validateUpsert: false, // default to turning validation off
1516
silenceWarnings: false,
@@ -41,6 +42,19 @@ export default (Model, bootOptions = {}) => {
4142
required: options.required,
4243
});
4344

45+
Model.defineProperty(options.deletedAt, {
46+
type: Date,
47+
required: false,
48+
mysql: {
49+
columnName: options.deletedAt,
50+
dataType: "timestamp",
51+
dataLength: null,
52+
dataPrecision: null,
53+
dataScale: null,
54+
nullable: "Y"
55+
}
56+
});
57+
4458
Model.observe('before save', (ctx, next) => {
4559
debug('ctx.options', ctx.options);
4660
if (ctx.options && ctx.options.skipUpdatedAt) { return next(); }
@@ -54,6 +68,28 @@ export default (Model, bootOptions = {}) => {
5468
}
5569
return next();
5670
});
71+
72+
/**
73+
* Watches destroyAll(), deleteAll(), destroyById() , deleteById(), prototype.destroy(), prototype.delete() methods
74+
* and instead of deleting object, sets properties deletedAt and isDeleted.
75+
*/
76+
Model.observe('before delete', function (ctx, next) {
77+
Model.updateAll(ctx.where, {[options.deletedAt]: new Date()}).then(function (result) {
78+
next(null);
79+
});
80+
});
81+
82+
/**
83+
* When ever model tries to access data, we add by default isDeleted: false to where query
84+
* if there is already in query isDeleted property, then we do not modify query
85+
*/
86+
Model.observe('access', function (ctx, next) {
87+
if (!ctx.query.isDeleted && (!ctx.query.where || ctx.query.where && JSON.stringify(ctx.query.where).indexOf('isDeleted') == -1)) {
88+
if (!ctx.query.where) ctx.query.where = {};
89+
ctx.query.where[options.deletedAt] = null;
90+
}
91+
next();
92+
});
5793
};
5894

5995
module.exports = exports.default;

0 commit comments

Comments
 (0)