ts-patch-mongoose 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +8 -3
- package/dist/cjs/patch.js +88 -0
- package/dist/cjs/patch.js.map +1 -0
- package/dist/cjs/plugin.js +71 -149
- package/dist/cjs/plugin.js.map +1 -1
- package/dist/cjs/types/interfaces/IPluginOptions.d.ts +1 -1
- package/dist/cjs/types/interfaces/IPluginOptions.d.ts.map +1 -1
- package/dist/cjs/types/patch.d.ts +12 -0
- package/dist/cjs/types/patch.d.ts.map +1 -0
- package/dist/cjs/types/plugin.d.ts.map +1 -1
- package/dist/esm/patch.js +79 -0
- package/dist/esm/patch.js.map +1 -0
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/plugin.mjs +68 -146
- package/dist/esm/types/interfaces/IPluginOptions.d.ts +1 -1
- package/dist/esm/types/interfaces/IPluginOptions.d.ts.map +1 -1
- package/dist/esm/types/patch.d.ts +12 -0
- package/dist/esm/types/patch.d.ts.map +1 -0
- package/dist/esm/types/plugin.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/interfaces/IPluginOptions.ts +1 -1
- package/src/patch.ts +101 -0
- package/src/plugin.ts +76 -156
- package/tests/em.test.ts +5 -5
- package/tests/patch.test.ts +141 -0
- package/tests/plugin-callback.test.ts +50 -0
- package/tests/plugin-event-created.test.ts +225 -76
- package/tests/plugin-event-deleted.test.ts +54 -47
- package/tests/plugin-event-updated.test.ts +139 -15
package/dist/esm/plugin.mjs
CHANGED
|
@@ -1,94 +1,37 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import omit from 'omit-deep';
|
|
3
|
-
import jsonpatch from 'fast-json-patch';
|
|
4
2
|
import { assign } from 'power-assign';
|
|
3
|
+
import { createPatch, updatePatch, deletePatch } from './patch';
|
|
5
4
|
import em from './em';
|
|
6
|
-
import History from './models/History';
|
|
7
5
|
const options = {
|
|
8
6
|
document: false,
|
|
9
7
|
query: true
|
|
10
8
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const lastHistory = await History.findOne({ collectionId: original._id }).sort('-version').exec();
|
|
34
|
-
if (lastHistory && lastHistory.version >= 0) {
|
|
35
|
-
version = lastHistory.version + 1;
|
|
36
|
-
}
|
|
37
|
-
await History.create({
|
|
38
|
-
op: context.op,
|
|
39
|
-
modelName: context.modelName,
|
|
40
|
-
collectionName: context.collectionName,
|
|
41
|
-
collectionId: original._id,
|
|
42
|
-
patch,
|
|
43
|
-
version
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
async function bulkPatch(opts, context, eventKey, docsKey) {
|
|
47
|
-
const event = opts[eventKey];
|
|
48
|
-
const docs = context[docsKey];
|
|
49
|
-
const key = eventKey === 'eventCreated' ? 'doc' : 'oldDoc';
|
|
50
|
-
if (_.isEmpty(docs) || (!event && opts.patchHistoryDisabled))
|
|
51
|
-
return;
|
|
52
|
-
const chunks = _.chunk(docs, 1000);
|
|
53
|
-
for await (const chunk of chunks) {
|
|
54
|
-
const bulk = [];
|
|
55
|
-
for (const doc of chunk) {
|
|
56
|
-
if (event)
|
|
57
|
-
em.emit(event, { [key]: doc });
|
|
58
|
-
if (!opts.patchHistoryDisabled) {
|
|
59
|
-
bulk.push({
|
|
60
|
-
insertOne: {
|
|
61
|
-
document: {
|
|
62
|
-
op: context.op,
|
|
63
|
-
modelName: context.modelName,
|
|
64
|
-
collectionName: context.collectionName,
|
|
65
|
-
collectionId: doc._id,
|
|
66
|
-
doc,
|
|
67
|
-
version: 0
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (!opts.patchHistoryDisabled) {
|
|
74
|
-
await History
|
|
75
|
-
.bulkWrite(bulk, { ordered: false })
|
|
76
|
-
.catch((err) => {
|
|
77
|
-
console.error(err);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
async function createPatch(opts, context) {
|
|
83
|
-
await bulkPatch(opts, context, 'eventCreated', 'createdDocs');
|
|
84
|
-
}
|
|
85
|
-
async function deletePatch(opts, context) {
|
|
86
|
-
await bulkPatch(opts, context, 'eventDeleted', 'deletedDocs');
|
|
87
|
-
}
|
|
9
|
+
const toObjectOptions = {
|
|
10
|
+
depopulate: true,
|
|
11
|
+
virtuals: false
|
|
12
|
+
};
|
|
13
|
+
const updateMethods = [
|
|
14
|
+
'update',
|
|
15
|
+
'updateOne',
|
|
16
|
+
'replaceOne',
|
|
17
|
+
'updateMany',
|
|
18
|
+
'findOneAndUpdate',
|
|
19
|
+
'findOneAndReplace',
|
|
20
|
+
'findByIdAndUpdate'
|
|
21
|
+
];
|
|
22
|
+
const deleteMethods = [
|
|
23
|
+
'remove',
|
|
24
|
+
'findOneAndDelete',
|
|
25
|
+
'findOneAndRemove',
|
|
26
|
+
'findByIdAndDelete',
|
|
27
|
+
'findByIdAndRemove',
|
|
28
|
+
'deleteOne',
|
|
29
|
+
'deleteMany'
|
|
30
|
+
];
|
|
88
31
|
export const patchEventEmitter = em;
|
|
89
32
|
export const patchHistoryPlugin = function plugin(schema, opts) {
|
|
90
33
|
schema.pre('save', async function (next) {
|
|
91
|
-
const current = this.toObject(
|
|
34
|
+
const current = this.toObject(toObjectOptions);
|
|
92
35
|
const model = this.constructor;
|
|
93
36
|
const context = {
|
|
94
37
|
op: this.isNew ? 'create' : 'update',
|
|
@@ -96,21 +39,16 @@ export const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
96
39
|
collectionName: opts.collectionName ?? model.collection.collectionName,
|
|
97
40
|
createdDocs: [current]
|
|
98
41
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
await createPatch(opts, context);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
const original = await model.findById(current._id).exec();
|
|
105
|
-
if (original) {
|
|
106
|
-
await updatePatch(opts, context, current, original);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
next();
|
|
42
|
+
if (this.isNew) {
|
|
43
|
+
await createPatch(opts, context);
|
|
110
44
|
}
|
|
111
|
-
|
|
112
|
-
|
|
45
|
+
else {
|
|
46
|
+
const original = await model.findById(current._id).exec();
|
|
47
|
+
if (original) {
|
|
48
|
+
await updatePatch(opts, context, current, original);
|
|
49
|
+
}
|
|
113
50
|
}
|
|
51
|
+
next();
|
|
114
52
|
});
|
|
115
53
|
schema.post('insertMany', async function (docs) {
|
|
116
54
|
const context = {
|
|
@@ -121,7 +59,7 @@ export const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
121
59
|
};
|
|
122
60
|
await createPatch(opts, context);
|
|
123
61
|
});
|
|
124
|
-
schema.pre(
|
|
62
|
+
schema.pre(updateMethods, async function (next) {
|
|
125
63
|
const filter = this.getFilter();
|
|
126
64
|
const update = this.getUpdate();
|
|
127
65
|
const options = this.getOptions();
|
|
@@ -133,70 +71,54 @@ export const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
133
71
|
collectionName: opts.collectionName ?? this.model.collection.collectionName,
|
|
134
72
|
isNew: options.upsert && count === 0
|
|
135
73
|
};
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
delete update[key];
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
const cursor = this.model.find(filter).cursor();
|
|
145
|
-
await cursor.eachAsync(async (doc) => {
|
|
146
|
-
let current = doc.toObject({ depopulate: true });
|
|
147
|
-
const original = doc.toObject({ depopulate: true });
|
|
148
|
-
current = assign(current, update);
|
|
149
|
-
_.forEach(commands, (command) => {
|
|
150
|
-
try {
|
|
151
|
-
current = assign(current, command);
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
await updatePatch(opts, this._context, current, original);
|
|
74
|
+
const keys = _.keys(update).filter((key) => key.startsWith('$'));
|
|
75
|
+
if (update && !_.isEmpty(keys)) {
|
|
76
|
+
_.forEach(keys, (key) => {
|
|
77
|
+
commands.push({ [key]: update[key] });
|
|
78
|
+
delete update[key];
|
|
157
79
|
});
|
|
158
|
-
next();
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
next(error);
|
|
162
80
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
this._context.createdDocs.push(current);
|
|
81
|
+
const cursor = this.model.find(filter).cursor();
|
|
82
|
+
await cursor.eachAsync(async (doc) => {
|
|
83
|
+
let current = doc.toObject(toObjectOptions);
|
|
84
|
+
const original = doc.toObject(toObjectOptions);
|
|
85
|
+
current = assign(current, update);
|
|
86
|
+
_.forEach(commands, (command) => {
|
|
87
|
+
try {
|
|
88
|
+
current = assign(current, command);
|
|
172
89
|
}
|
|
173
|
-
|
|
174
|
-
this._context.createdDocs = [current];
|
|
90
|
+
catch (error) {
|
|
175
91
|
}
|
|
176
92
|
});
|
|
93
|
+
await updatePatch(opts, this._context, current, original);
|
|
94
|
+
});
|
|
95
|
+
next();
|
|
96
|
+
});
|
|
97
|
+
schema.post(updateMethods, async function () {
|
|
98
|
+
const update = this.getUpdate();
|
|
99
|
+
if (!update || !this._context.isNew)
|
|
100
|
+
return;
|
|
101
|
+
const found = await this.model.findOne(update).exec();
|
|
102
|
+
if (found) {
|
|
103
|
+
const current = found.toObject(toObjectOptions);
|
|
104
|
+
this._context.createdDocs = [current];
|
|
177
105
|
await createPatch(opts, this._context);
|
|
178
106
|
}
|
|
179
107
|
});
|
|
180
|
-
schema.
|
|
181
|
-
const original = this.toObject(
|
|
108
|
+
schema.post('remove', async function () {
|
|
109
|
+
const original = this.toObject(toObjectOptions);
|
|
182
110
|
const model = this.constructor;
|
|
183
111
|
const context = {
|
|
184
112
|
op: 'delete',
|
|
185
113
|
modelName: opts.modelName ?? model.modelName,
|
|
186
114
|
collectionName: opts.collectionName ?? model.collection.collectionName
|
|
187
115
|
};
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
em.emit(opts.eventDeleted, { oldDoc: original });
|
|
191
|
-
}
|
|
192
|
-
await deletePatch(opts, context);
|
|
193
|
-
next();
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
next(error);
|
|
116
|
+
if (opts.eventDeleted) {
|
|
117
|
+
em.emit(opts.eventDeleted, { oldDoc: original });
|
|
197
118
|
}
|
|
119
|
+
await deletePatch(opts, context);
|
|
198
120
|
});
|
|
199
|
-
schema.pre(
|
|
121
|
+
schema.pre(deleteMethods, options, async function (next) {
|
|
200
122
|
const filter = this.getFilter();
|
|
201
123
|
const options = this.getOptions();
|
|
202
124
|
const ignore = options.__ignore;
|
|
@@ -219,12 +141,12 @@ export const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
219
141
|
this._context.deletedDocs = [doc];
|
|
220
142
|
}
|
|
221
143
|
}
|
|
222
|
-
if (opts.
|
|
223
|
-
await opts.
|
|
144
|
+
if (opts.preDeleteCallback && _.isArray(this._context.deletedDocs) && !_.isEmpty(this._context.deletedDocs)) {
|
|
145
|
+
await opts.preDeleteCallback(this._context.deletedDocs);
|
|
224
146
|
}
|
|
225
147
|
next();
|
|
226
148
|
});
|
|
227
|
-
schema.post(
|
|
149
|
+
schema.post(deleteMethods, options, async function () {
|
|
228
150
|
await deletePatch(opts, this._context);
|
|
229
151
|
});
|
|
230
152
|
};
|
|
@@ -6,7 +6,7 @@ interface IPluginOptions<T> {
|
|
|
6
6
|
eventCreated?: string;
|
|
7
7
|
eventDeleted?: string;
|
|
8
8
|
patchHistoryDisabled?: boolean;
|
|
9
|
-
|
|
9
|
+
preDeleteCallback?: (docs: HydratedDocument<T>[]) => Promise<void>;
|
|
10
10
|
omit?: string[];
|
|
11
11
|
}
|
|
12
12
|
export default IPluginOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IPluginOptions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/IPluginOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,UAAU,cAAc,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,
|
|
1
|
+
{"version":3,"file":"IPluginOptions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/IPluginOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,UAAU,cAAc,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HydratedDocument } from 'mongoose';
|
|
2
|
+
import type IPluginOptions from './interfaces/IPluginOptions';
|
|
3
|
+
import type IContext from './interfaces/IContext';
|
|
4
|
+
export declare function getObjects<T>(opts: IPluginOptions<T>, current: HydratedDocument<T>, original: HydratedDocument<T>): {
|
|
5
|
+
currentObject: Partial<T>;
|
|
6
|
+
originalObject: Partial<T>;
|
|
7
|
+
};
|
|
8
|
+
export declare function bulkPatch<T>(opts: IPluginOptions<T>, context: IContext<T>, eventKey: 'eventCreated' | 'eventDeleted', docsKey: 'createdDocs' | 'deletedDocs'): Promise<void>;
|
|
9
|
+
export declare function createPatch<T>(opts: IPluginOptions<T>, context: IContext<T>): Promise<void>;
|
|
10
|
+
export declare function updatePatch<T>(opts: IPluginOptions<T>, context: IContext<T>, current: HydratedDocument<T>, original: HydratedDocument<T>): Promise<void>;
|
|
11
|
+
export declare function deletePatch<T>(opts: IPluginOptions<T>, context: IContext<T>): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../../src/patch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,UAAU,CAAA;AAEvD,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAC7D,OAAO,KAAK,QAAQ,MAAM,uBAAuB,CAAA;AAKjD,wBAAgB,UAAU,CAAC,CAAC,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG;IAAE,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,CAU9K;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,GAAG,cAAc,EAAE,OAAO,EAAE,aAAa,GAAG,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCnL;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAElG;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B/J;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAElG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAGA,OAAO,KAAK,EAAoB,KAAK,EAA2B,MAAM,EAAmB,MAAM,UAAU,CAAA;AAEzG,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAwC7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;CAAK,CAAA;AAQnC,eAAO,MAAM,kBAAkB,oOAAoE,IAgJlG,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-patch-mongoose",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Patch history & events for mongoose models",
|
|
5
5
|
"author": "Alex Eagle",
|
|
6
6
|
"license": "MIT",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@shelf/jest-mongodb": "4.1.7",
|
|
85
85
|
"@swc/cli": "0.1.62",
|
|
86
|
-
"@swc/core": "1.3.
|
|
86
|
+
"@swc/core": "1.3.50",
|
|
87
87
|
"@swc/helpers": "0.5.0",
|
|
88
|
-
"@swc/jest": "0.2.
|
|
88
|
+
"@swc/jest": "0.2.26",
|
|
89
89
|
"@swc/register": "0.1.10",
|
|
90
90
|
"@types/jest": "29.5.0",
|
|
91
|
-
"@types/lodash": "4.14.
|
|
91
|
+
"@types/lodash": "4.14.194",
|
|
92
92
|
"@types/node": "18.15.11",
|
|
93
93
|
"@typescript-eslint/eslint-plugin": "5.58.0",
|
|
94
94
|
"@typescript-eslint/parser": "5.58.0",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"eslint-plugin-n": "15.7.0",
|
|
101
101
|
"eslint-plugin-node": "11.1.0",
|
|
102
102
|
"eslint-plugin-promise": "6.1.1",
|
|
103
|
+
"eslint-plugin-sonarjs": "0.19.0",
|
|
103
104
|
"jest": "29.5.0",
|
|
104
105
|
"merge": "2.1.1",
|
|
105
106
|
"mongoose": "6.10.5",
|
|
@@ -7,7 +7,7 @@ interface IPluginOptions<T> {
|
|
|
7
7
|
eventCreated?: string
|
|
8
8
|
eventDeleted?: string
|
|
9
9
|
patchHistoryDisabled?: boolean
|
|
10
|
-
|
|
10
|
+
preDeleteCallback?: (docs: HydratedDocument<T>[]) => Promise<void>
|
|
11
11
|
omit?: string[]
|
|
12
12
|
}
|
|
13
13
|
|
package/src/patch.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import omit from 'omit-deep'
|
|
3
|
+
import jsonpatch from 'fast-json-patch'
|
|
4
|
+
|
|
5
|
+
import type { HydratedDocument, Types } from 'mongoose'
|
|
6
|
+
|
|
7
|
+
import type IPluginOptions from './interfaces/IPluginOptions'
|
|
8
|
+
import type IContext from './interfaces/IContext'
|
|
9
|
+
|
|
10
|
+
import History from './models/History'
|
|
11
|
+
import em from './em'
|
|
12
|
+
|
|
13
|
+
export function getObjects<T> (opts: IPluginOptions<T>, current: HydratedDocument<T>, original: HydratedDocument<T>): { currentObject: Partial<T>, originalObject: Partial<T> } {
|
|
14
|
+
let currentObject = JSON.parse(JSON.stringify(current)) as Partial<T>
|
|
15
|
+
let originalObject = JSON.parse(JSON.stringify(original)) as Partial<T>
|
|
16
|
+
|
|
17
|
+
if (opts.omit) {
|
|
18
|
+
currentObject = omit(currentObject, opts.omit)
|
|
19
|
+
originalObject = omit(originalObject, opts.omit)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { currentObject, originalObject }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function bulkPatch<T> (opts: IPluginOptions<T>, context: IContext<T>, eventKey: 'eventCreated' | 'eventDeleted', docsKey: 'createdDocs' | 'deletedDocs'): Promise<void> {
|
|
26
|
+
const event = opts[eventKey]
|
|
27
|
+
const docs = context[docsKey]
|
|
28
|
+
const key = eventKey === 'eventCreated' ? 'doc' : 'oldDoc'
|
|
29
|
+
|
|
30
|
+
if (_.isEmpty(docs) || (!event && opts.patchHistoryDisabled)) return
|
|
31
|
+
|
|
32
|
+
const chunks = _.chunk(docs, 1000)
|
|
33
|
+
|
|
34
|
+
for await (const chunk of chunks) {
|
|
35
|
+
const bulk = []
|
|
36
|
+
|
|
37
|
+
for (const doc of chunk) {
|
|
38
|
+
if (event) em.emit(event, { [key]: doc })
|
|
39
|
+
|
|
40
|
+
if (!opts.patchHistoryDisabled) {
|
|
41
|
+
bulk.push({
|
|
42
|
+
insertOne: {
|
|
43
|
+
document: {
|
|
44
|
+
op: context.op,
|
|
45
|
+
modelName: context.modelName,
|
|
46
|
+
collectionName: context.collectionName,
|
|
47
|
+
collectionId: doc._id as Types.ObjectId,
|
|
48
|
+
doc,
|
|
49
|
+
version: 0
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!opts.patchHistoryDisabled) {
|
|
57
|
+
await History.bulkWrite(bulk, { ordered: false })
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function createPatch<T> (opts: IPluginOptions<T>, context: IContext<T>): Promise<void> {
|
|
63
|
+
await bulkPatch(opts, context, 'eventCreated', 'createdDocs')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function updatePatch<T> (opts: IPluginOptions<T>, context: IContext<T>, current: HydratedDocument<T>, original: HydratedDocument<T>): Promise<void> {
|
|
67
|
+
const { currentObject, originalObject } = getObjects(opts, current, original)
|
|
68
|
+
|
|
69
|
+
if (_.isEmpty(originalObject) || _.isEmpty(currentObject)) return
|
|
70
|
+
|
|
71
|
+
const patch = jsonpatch.compare(originalObject, currentObject, true)
|
|
72
|
+
|
|
73
|
+
if (_.isEmpty(patch)) return
|
|
74
|
+
|
|
75
|
+
if (opts.eventUpdated) {
|
|
76
|
+
em.emit(opts.eventUpdated, { oldDoc: original, doc: current, patch })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (opts.patchHistoryDisabled) return
|
|
80
|
+
|
|
81
|
+
let version = 0
|
|
82
|
+
|
|
83
|
+
const lastHistory = await History.findOne({ collectionId: original._id as Types.ObjectId }).sort('-version').exec()
|
|
84
|
+
|
|
85
|
+
if (lastHistory && lastHistory.version >= 0) {
|
|
86
|
+
version = lastHistory.version + 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await History.create({
|
|
90
|
+
op: context.op,
|
|
91
|
+
modelName: context.modelName,
|
|
92
|
+
collectionName: context.collectionName,
|
|
93
|
+
collectionId: original._id as Types.ObjectId,
|
|
94
|
+
patch,
|
|
95
|
+
version
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function deletePatch<T> (opts: IPluginOptions<T>, context: IContext<T>): Promise<void> {
|
|
100
|
+
await bulkPatch(opts, context, 'eventDeleted', 'deletedDocs')
|
|
101
|
+
}
|