oak-domain 4.2.3 → 4.2.4
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/lib/base-app-domain/ActionDefDict.d.ts +8 -8
- package/lib/base-app-domain/Oper/Schema.d.ts +158 -158
- package/lib/base-app-domain/Oper/Storage.js +56 -56
- package/lib/base-app-domain/User/Action.d.ts +11 -11
- package/lib/base-app-domain/User/Action.js +12 -12
- package/lib/base-app-domain/UserEntityGrant/Action.d.ts +5 -5
- package/lib/base-app-domain/UserEntityGrant/Action.js +5 -5
- package/lib/compiler/schemalBuilder.js +4167 -4167
- package/lib/entities/Oper.d.ts +12 -12
- package/lib/entities/Oper.js +36 -36
- package/lib/entities/User.d.ts +18 -18
- package/lib/entities/User.js +32 -32
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -3
- package/lib/store/AsyncRowStore.d.ts +66 -66
- package/lib/store/CascadeStore.d.ts +109 -109
- package/lib/store/CascadeStore.js +1728 -1728
- package/lib/store/RelationAuth.js +1209 -1209
- package/lib/store/TriggerExecutor.js +468 -468
- package/lib/store/actionDef.js +278 -278
- package/lib/store/checker.js +487 -487
- package/lib/store/relation.d.ts +12 -12
- package/lib/store/relation.js +74 -74
- package/lib/triggers/index.d.ts +5 -5
- package/lib/triggers/index.js +28 -28
- package/lib/types/Configuration.d.ts +42 -42
- package/lib/types/Configuration.js +3 -3
- package/lib/types/Connector.d.ts +39 -39
- package/lib/types/Entity.d.ts +209 -209
- package/lib/types/Sync.d.ts +74 -68
- package/lib/types/Sync.js +9 -9
- package/lib/types/index.d.ts +27 -27
- package/lib/types/index.js +30 -30
- package/lib/utils/SimpleConnector.d.ts +81 -81
- package/lib/utils/SimpleConnector.js +217 -217
- package/lib/utils/assert.d.ts +5 -5
- package/lib/utils/projection.d.ts +4 -4
- package/lib/utils/relationPath.d.ts +31 -31
- package/lib/utils/relationPath.js +202 -202
- package/package.json +51 -51
- package/src/entities/ActionAuth.ts +41 -41
- package/src/entities/I18n.ts +45 -45
- package/src/entities/Modi.ts +69 -69
- package/src/entities/ModiEntity.ts +26 -26
- package/src/entities/Oper.ts +48 -48
- package/src/entities/OperEntity.ts +27 -27
- package/src/entities/Path.ts +43 -43
- package/src/entities/Relation.ts +43 -43
- package/src/entities/RelationAuth.ts +44 -44
- package/src/entities/User.ts +48 -48
- package/src/entities/UserEntityClaim.ts +29 -29
- package/src/entities/UserEntityGrant.ts +24 -24
- package/src/entities/UserRelation.ts +50 -50
|
@@ -1,468 +1,468 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TriggerExecutor = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
-
const lodash_1 = require("../utils/lodash");
|
|
7
|
-
const filter_1 = require("../store/filter");
|
|
8
|
-
const Entity_1 = require("../types/Entity");
|
|
9
|
-
const Trigger_1 = require("../types/Trigger");
|
|
10
|
-
const SyncRowStore_1 = require("./SyncRowStore");
|
|
11
|
-
const checker_1 = require("./checker");
|
|
12
|
-
const uuid_1 = require("../utils/uuid");
|
|
13
|
-
/**
|
|
14
|
-
* update可能会传入多种不同的action,此时都需要检查update trigger
|
|
15
|
-
*/
|
|
16
|
-
/* const UnifiedActionMatrix: Record<string, string> = {
|
|
17
|
-
'create': 'create',
|
|
18
|
-
'remove': 'remove',
|
|
19
|
-
'select': 'select',
|
|
20
|
-
'download': 'select',
|
|
21
|
-
'count': 'select',
|
|
22
|
-
'stat': 'select',
|
|
23
|
-
}; */
|
|
24
|
-
class TriggerExecutor {
|
|
25
|
-
counter;
|
|
26
|
-
triggerMap;
|
|
27
|
-
triggerNameMap;
|
|
28
|
-
volatileEntities;
|
|
29
|
-
logger;
|
|
30
|
-
contextBuilder;
|
|
31
|
-
onVolatileTrigger;
|
|
32
|
-
constructor(contextBuilder, logger = console, onVolatileTrigger) {
|
|
33
|
-
this.contextBuilder = contextBuilder;
|
|
34
|
-
this.logger = logger;
|
|
35
|
-
this.triggerMap = {};
|
|
36
|
-
this.triggerNameMap = {};
|
|
37
|
-
this.volatileEntities = [];
|
|
38
|
-
this.counter = 0;
|
|
39
|
-
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
|
40
|
-
const context = await this.contextBuilder(cxtStr);
|
|
41
|
-
await context.begin();
|
|
42
|
-
try {
|
|
43
|
-
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
|
44
|
-
await context.commit();
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
await context.rollback();
|
|
48
|
-
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
setOnVolatileTrigger(onVolatileTrigger) {
|
|
53
|
-
this.onVolatileTrigger = onVolatileTrigger;
|
|
54
|
-
}
|
|
55
|
-
registerChecker(checker) {
|
|
56
|
-
const { entity, action, type, conditionalFilter, mt } = checker;
|
|
57
|
-
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
|
|
58
|
-
const { fn, when } = (0, checker_1.translateCheckerInAsyncContext)(checker);
|
|
59
|
-
const trigger = {
|
|
60
|
-
checkerType: type,
|
|
61
|
-
name: triggerName,
|
|
62
|
-
priority: checker.priority || Trigger_1.CHECKER_PRIORITY_MAP[type],
|
|
63
|
-
entity,
|
|
64
|
-
action: action,
|
|
65
|
-
fn,
|
|
66
|
-
when,
|
|
67
|
-
mt,
|
|
68
|
-
filter: conditionalFilter,
|
|
69
|
-
};
|
|
70
|
-
this.registerTrigger(trigger);
|
|
71
|
-
}
|
|
72
|
-
/* getCheckers<T extends keyof ED>(entity: T, action: ED[T]['Action'], checkerTypes?: CheckerType[]) {
|
|
73
|
-
const triggers = this.triggerMap[entity] && this.triggerMap[entity]![action]?.filter(
|
|
74
|
-
trigger => (typeof trigger.action === 'string' && trigger.action === action || trigger.action instanceof Array && trigger.action.includes(action as any)
|
|
75
|
-
&& (!checkerTypes || trigger.checkerType && checkerTypes.includes(trigger.checkerType)))
|
|
76
|
-
);
|
|
77
|
-
return triggers;
|
|
78
|
-
} */
|
|
79
|
-
registerTrigger(trigger) {
|
|
80
|
-
// trigger的两种访问方式: by name, by entity/action
|
|
81
|
-
if (this.triggerNameMap.hasOwnProperty(trigger.name)) {
|
|
82
|
-
throw new Error(`不可有同名的触发器「${trigger.name}」`);
|
|
83
|
-
}
|
|
84
|
-
if (typeof trigger.priority !== 'number') {
|
|
85
|
-
trigger.priority = Trigger_1.TRIGGER_DEFAULT_PRIORITY; // 默认值
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
(0, assert_1.default)(trigger.priority <= Trigger_1.CHECKER_MAX_PRIORITY && trigger.priority >= Trigger_1.TRIGGER_MIN_PRIORITY, `trigger「${trigger.name}」的优先级定义越界,应该在${Trigger_1.TRIGGER_MIN_PRIORITY}到${Trigger_1.CHECKER_MAX_PRIORITY}之间`);
|
|
89
|
-
}
|
|
90
|
-
if (trigger.filter) {
|
|
91
|
-
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
|
|
92
|
-
|| trigger.action instanceof Array && !trigger.action.includes('create'), `trigger【${trigger.name}】是create类型但却带有filter`);
|
|
93
|
-
(0, assert_1.default)(trigger.when === 'before' || trigger.when === 'commit', `定义了filter的trigger【${trigger.name}】的when只能是before或者commit`);
|
|
94
|
-
}
|
|
95
|
-
Object.assign(this.triggerNameMap, {
|
|
96
|
-
[trigger.name]: trigger,
|
|
97
|
-
});
|
|
98
|
-
const addTrigger = (action) => {
|
|
99
|
-
const triggers = this.triggerMap[trigger.entity] && this.triggerMap[trigger.entity][action];
|
|
100
|
-
if (triggers) {
|
|
101
|
-
let idx;
|
|
102
|
-
// 这里可以保持有序插入,后面取trigger的时候就不用排序了
|
|
103
|
-
for (idx = 0; idx < triggers.length; idx++) {
|
|
104
|
-
if (triggers[idx].priority > trigger.priority) {
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
triggers.splice(idx, 0, trigger);
|
|
109
|
-
}
|
|
110
|
-
else if (this.triggerMap[trigger.entity]) {
|
|
111
|
-
Object.assign(this.triggerMap[trigger.entity], {
|
|
112
|
-
[action]: [trigger],
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
Object.assign(this.triggerMap, {
|
|
117
|
-
[trigger.entity]: {
|
|
118
|
-
[action]: [trigger],
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
if (typeof trigger.action === 'string') {
|
|
124
|
-
addTrigger(trigger.action);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
trigger.action.forEach(ele => addTrigger(ele));
|
|
128
|
-
}
|
|
129
|
-
if (trigger.when === 'commit' && trigger.strict === 'makeSure') {
|
|
130
|
-
if (this.volatileEntities.indexOf(trigger.entity) === -1) {
|
|
131
|
-
this.volatileEntities.push(trigger.entity);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
unregisterTrigger(trigger) {
|
|
136
|
-
(0, assert_1.default)(trigger.when !== 'commit' || trigger.strict !== 'makeSure', 'could not remove strict volatile triggers');
|
|
137
|
-
const removeTrigger = (action) => {
|
|
138
|
-
const triggers = this.triggerMap[trigger.entity] && this.triggerMap[trigger.entity][action];
|
|
139
|
-
if (triggers) {
|
|
140
|
-
(0, lodash_1.pull)(triggers, trigger);
|
|
141
|
-
(0, lodash_1.unset)(this.triggerNameMap, trigger.name);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
if (typeof trigger.action === 'string') {
|
|
145
|
-
removeTrigger(trigger.action);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
trigger.action.forEach(ele => removeTrigger(ele));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
async preCommitTrigger(entity, operation, trigger, context, option) {
|
|
152
|
-
(0, assert_1.default)(trigger.action !== 'select');
|
|
153
|
-
(0, assert_1.default)(trigger.when === 'commit');
|
|
154
|
-
if (trigger.strict === 'makeSure') {
|
|
155
|
-
const uuid = await (0, uuid_1.generateNewIdAsync)();
|
|
156
|
-
const cxtStr = context.toString();
|
|
157
|
-
const { data } = operation;
|
|
158
|
-
switch (operation.action) {
|
|
159
|
-
case 'create': {
|
|
160
|
-
if (data instanceof Array) {
|
|
161
|
-
data.forEach((d) => {
|
|
162
|
-
if (d.hasOwnProperty(Entity_1.TriggerDataAttribute) || d.hasOwnProperty(Entity_1.TriggerUuidAttribute)) {
|
|
163
|
-
throw new Error('同一行数据上不能同时存在两个跨事务约束');
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
if (data.hasOwnProperty(Entity_1.TriggerDataAttribute) || data.hasOwnProperty(Entity_1.TriggerUuidAttribute)) {
|
|
169
|
-
throw new Error('同一行数据上不能存在两个跨事务约束');
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
default: {
|
|
175
|
-
const { filter } = operation;
|
|
176
|
-
// 此时要保证更新或者删除的行上没有跨事务约束
|
|
177
|
-
const filter2 = (0, filter_1.combineFilters)(entity, context.getSchema(), [{
|
|
178
|
-
[Entity_1.TriggerUuidAttribute]: {
|
|
179
|
-
$exists: true,
|
|
180
|
-
},
|
|
181
|
-
}, filter]);
|
|
182
|
-
const count = await context.count(entity, {
|
|
183
|
-
filter: filter2
|
|
184
|
-
}, {});
|
|
185
|
-
if (count > 0) {
|
|
186
|
-
throw new Error(`对象${String(entity)}的行「${JSON.stringify(operation)}」上已经存在未完成的跨事务约束`);
|
|
187
|
-
}
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (data instanceof Array) {
|
|
192
|
-
data.forEach((d) => {
|
|
193
|
-
Object.assign(d, {
|
|
194
|
-
[Entity_1.TriggerDataAttribute]: {
|
|
195
|
-
name: trigger.name,
|
|
196
|
-
cxtStr
|
|
197
|
-
option,
|
|
198
|
-
},
|
|
199
|
-
[Entity_1.TriggerUuidAttribute]: uuid,
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
Object.assign(data, {
|
|
205
|
-
[Entity_1.TriggerDataAttribute]: {
|
|
206
|
-
name: trigger.name,
|
|
207
|
-
cxtStr,
|
|
208
|
-
option,
|
|
209
|
-
},
|
|
210
|
-
[Entity_1.TriggerUuidAttribute]: uuid,
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
postCommitTrigger(entity, operation, trigger, context, option) {
|
|
216
|
-
context.on('commit', async () => {
|
|
217
|
-
let ids = [];
|
|
218
|
-
const { opRecords } = context;
|
|
219
|
-
if (operation.action === 'create') {
|
|
220
|
-
const { data } = operation;
|
|
221
|
-
if (data instanceof Array) {
|
|
222
|
-
ids = data.map(ele => ele.id);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
ids = [data.id];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
const record = opRecords.find(ele => ele.id === operation.id);
|
|
230
|
-
// 目前框架在operation时,一定会将ids记录在operation当中(见CascadeStore中的doUpdateSingleRowAsync函数
|
|
231
|
-
(0, assert_1.default)(record && record.a !== 'c');
|
|
232
|
-
const { f } = record;
|
|
233
|
-
ids = f.id.$in;
|
|
234
|
-
}
|
|
235
|
-
const cxtStr = await context.toString();
|
|
236
|
-
this.onVolatileTrigger(entity, trigger, ids, cxtStr, option);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
preOperation(entity, operation, context, option) {
|
|
240
|
-
const { action } = operation;
|
|
241
|
-
const triggers = this.triggerMap[entity] && this.triggerMap[entity][action]?.filter(trigger => (typeof trigger.action === 'string' && trigger.action === operation.action
|
|
242
|
-
|| trigger.action instanceof Array && trigger.action.includes(operation.action))
|
|
243
|
-
// 加上modi的过滤条件
|
|
244
|
-
&& this.judgeModiTurn(option, trigger));
|
|
245
|
-
if (triggers) {
|
|
246
|
-
const preTriggers = triggers.filter(ele => ele.when === 'before' && (!ele.check || ele.check(operation)));
|
|
247
|
-
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
248
|
-
(!ele.check || ele.check(operation)));
|
|
249
|
-
if (context instanceof SyncRowStore_1.SyncContext) {
|
|
250
|
-
for (const trigger of preTriggers) {
|
|
251
|
-
if (trigger.filter) {
|
|
252
|
-
// trigger只对满足条件的前项进行判断,如果确定不满足可以pass
|
|
253
|
-
(0, assert_1.default)(operation.action !== 'create');
|
|
254
|
-
const { filter } = trigger;
|
|
255
|
-
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
|
256
|
-
(0, assert_1.default)(!(filterr instanceof Promise));
|
|
257
|
-
const filterRepelled = (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
258
|
-
if (filterRepelled) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
const number = trigger.fn({ operation: operation }, context, option);
|
|
263
|
-
if (number > 0) {
|
|
264
|
-
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
(0, assert_1.default)(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
// 异步context
|
|
271
|
-
const execPreTrigger = async (idx) => {
|
|
272
|
-
if (idx >= preTriggers.length) {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const trigger = preTriggers[idx];
|
|
276
|
-
if (trigger.filter) {
|
|
277
|
-
(0, assert_1.default)(operation.action !== 'create');
|
|
278
|
-
const { filter } = trigger;
|
|
279
|
-
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
280
|
-
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
281
|
-
if (filterRepelled) {
|
|
282
|
-
return execPreTrigger(idx + 1);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const number = await trigger.fn({ operation: operation }, context, option);
|
|
286
|
-
if (number > 0) {
|
|
287
|
-
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
288
|
-
}
|
|
289
|
-
return execPreTrigger(idx + 1);
|
|
290
|
-
};
|
|
291
|
-
const execCommitTrigger = async (idx) => {
|
|
292
|
-
if (idx >= commitTriggers.length) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const trigger = commitTriggers[idx];
|
|
296
|
-
if (trigger.filter) {
|
|
297
|
-
(0, assert_1.default)(operation.action !== 'create');
|
|
298
|
-
const { filter } = trigger;
|
|
299
|
-
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
300
|
-
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
301
|
-
if (filterRepelled) {
|
|
302
|
-
return execCommitTrigger(idx + 1);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
await this.preCommitTrigger(entity, operation, trigger, context, option);
|
|
306
|
-
return execCommitTrigger(idx + 1);
|
|
307
|
-
};
|
|
308
|
-
return execPreTrigger(0)
|
|
309
|
-
.then(() => execCommitTrigger(0));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async execVolatileTrigger(entity, name, ids, context, option) {
|
|
314
|
-
const trigger = this.triggerNameMap[name];
|
|
315
|
-
(0, assert_1.default)(trigger && trigger.when === 'commit');
|
|
316
|
-
(0, assert_1.default)(ids.length > 0);
|
|
317
|
-
const { fn } = trigger;
|
|
318
|
-
await fn({ ids }, context, option);
|
|
319
|
-
if (trigger.strict === 'makeSure') {
|
|
320
|
-
try {
|
|
321
|
-
await context.operate(entity, {
|
|
322
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
323
|
-
action: 'update',
|
|
324
|
-
data: {
|
|
325
|
-
[Entity_1.TriggerDataAttribute]: null,
|
|
326
|
-
[Entity_1.TriggerUuidAttribute]: null,
|
|
327
|
-
},
|
|
328
|
-
filter: {
|
|
329
|
-
id: {
|
|
330
|
-
$in: ids,
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}, { includedDeleted: true, blockTrigger: true });
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
throw err;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* 判断一个trigger和当前modi上下文是否符合
|
|
342
|
-
* trigger的默认行为是:如果是commit时机的trigger,不显式声明则只能在modi apply时执行(create时不执行);非commit时机的trigger,不显式声明则只在modi create时执行
|
|
343
|
-
* @param option
|
|
344
|
-
* @param trigger
|
|
345
|
-
* @returns
|
|
346
|
-
*/
|
|
347
|
-
judgeModiTurn(option, trigger) {
|
|
348
|
-
const { mt, when } = trigger;
|
|
349
|
-
if (option.modiParentEntity) {
|
|
350
|
-
// 在创建modi过程中,标识为apply或者未标识但为commit时执行的trigger默认不能执行
|
|
351
|
-
return mt && ['both', 'create'].includes(mt) || !mt && when !== 'commit';
|
|
352
|
-
}
|
|
353
|
-
else if (option.applyingModi) {
|
|
354
|
-
// 在应用modi过程中,标识为create或者未标识但不为commit时执行的trigger默认不能执行
|
|
355
|
-
return mt && ['both', 'apply'].includes(mt) || !mt && when === 'commit';
|
|
356
|
-
}
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
postOperation(entity, operation, context, option, result) {
|
|
360
|
-
const { action } = operation;
|
|
361
|
-
const triggers = this.triggerMap[entity] && this.triggerMap[entity][action]?.filter(trigger => (typeof trigger.action === 'string' && trigger.action === operation.action
|
|
362
|
-
|| trigger.action instanceof Array && trigger.action.includes(operation.action))
|
|
363
|
-
// 加上modi的过滤条件
|
|
364
|
-
&& this.judgeModiTurn(option, trigger));
|
|
365
|
-
if (triggers) {
|
|
366
|
-
const postTriggers = triggers.filter(ele => ele.when === 'after' && (!ele.check || ele.check(operation)));
|
|
367
|
-
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
368
|
-
(!ele.check || ele.check(operation)));
|
|
369
|
-
if (context instanceof SyncRowStore_1.SyncContext) {
|
|
370
|
-
for (const trigger of postTriggers) {
|
|
371
|
-
const number = trigger.fn({
|
|
372
|
-
operation: operation,
|
|
373
|
-
result: result,
|
|
374
|
-
}, context, option);
|
|
375
|
-
if (number > 0) {
|
|
376
|
-
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
// 异步context
|
|
382
|
-
const execPostTrigger = async (idx) => {
|
|
383
|
-
if (idx >= postTriggers.length) {
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
const trigger = postTriggers[idx];
|
|
387
|
-
const number = await trigger.fn({
|
|
388
|
-
operation: operation,
|
|
389
|
-
result: result,
|
|
390
|
-
}, context, option);
|
|
391
|
-
if (number > 0) {
|
|
392
|
-
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
393
|
-
}
|
|
394
|
-
return execPostTrigger(idx + 1);
|
|
395
|
-
};
|
|
396
|
-
const execCommitTrigger = async (idx) => {
|
|
397
|
-
if (idx >= commitTriggers.length) {
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
const trigger = commitTriggers[idx];
|
|
401
|
-
if (trigger.filter) {
|
|
402
|
-
(0, assert_1.default)(operation.action !== 'create');
|
|
403
|
-
const { filter } = trigger;
|
|
404
|
-
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
405
|
-
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
406
|
-
if (filterRepelled) {
|
|
407
|
-
return execCommitTrigger(idx + 1);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
this.postCommitTrigger(entity, operation, trigger, context, option);
|
|
411
|
-
return execCommitTrigger(idx + 1);
|
|
412
|
-
};
|
|
413
|
-
return execPostTrigger(0)
|
|
414
|
-
.then(() => execCommitTrigger(0));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
async checkpoint(timestamp) {
|
|
419
|
-
let result = 0;
|
|
420
|
-
for (const entity of this.volatileEntities) {
|
|
421
|
-
const filter = {
|
|
422
|
-
[Entity_1.TriggerUuidAttribute]: {
|
|
423
|
-
$exists: true,
|
|
424
|
-
},
|
|
425
|
-
[Entity_1.UpdateAtAttribute]: {
|
|
426
|
-
$lt: timestamp,
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
const context = await this.contextBuilder();
|
|
430
|
-
if (context.clusterInfo?.usingCluster) {
|
|
431
|
-
const { instanceCount, instanceId } = context.clusterInfo;
|
|
432
|
-
filter.$$seq$$ = {
|
|
433
|
-
$mod: [instanceCount, instanceId],
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
await context.begin();
|
|
437
|
-
try {
|
|
438
|
-
const rows = await context.select(entity, {
|
|
439
|
-
data: {
|
|
440
|
-
id: 1,
|
|
441
|
-
[Entity_1.TriggerDataAttribute]: 1,
|
|
442
|
-
[Entity_1.TriggerUuidAttribute]: 1,
|
|
443
|
-
},
|
|
444
|
-
filter,
|
|
445
|
-
}, {
|
|
446
|
-
includedDeleted: true,
|
|
447
|
-
dontCollect: true,
|
|
448
|
-
forUpdate:
|
|
449
|
-
});
|
|
450
|
-
const grouped = (0, lodash_1.groupBy)(rows, Entity_1.TriggerUuidAttribute);
|
|
451
|
-
for (const uuid in grouped) {
|
|
452
|
-
const rs = grouped[uuid];
|
|
453
|
-
const { [Entity_1.TriggerDataAttribute]: triggerData } = rs[0];
|
|
454
|
-
const { name, cxtStr, option } = triggerData;
|
|
455
|
-
|
|
456
|
-
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id), context, option);
|
|
457
|
-
}
|
|
458
|
-
await context.commit();
|
|
459
|
-
}
|
|
460
|
-
catch (err) {
|
|
461
|
-
await context.rollback();
|
|
462
|
-
this.logger.error(`执行checkpoint时出错,对象是「${entity}」,异常是`, err);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return result;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
exports.TriggerExecutor = TriggerExecutor;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TriggerExecutor = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
+
const lodash_1 = require("../utils/lodash");
|
|
7
|
+
const filter_1 = require("../store/filter");
|
|
8
|
+
const Entity_1 = require("../types/Entity");
|
|
9
|
+
const Trigger_1 = require("../types/Trigger");
|
|
10
|
+
const SyncRowStore_1 = require("./SyncRowStore");
|
|
11
|
+
const checker_1 = require("./checker");
|
|
12
|
+
const uuid_1 = require("../utils/uuid");
|
|
13
|
+
/**
|
|
14
|
+
* update可能会传入多种不同的action,此时都需要检查update trigger
|
|
15
|
+
*/
|
|
16
|
+
/* const UnifiedActionMatrix: Record<string, string> = {
|
|
17
|
+
'create': 'create',
|
|
18
|
+
'remove': 'remove',
|
|
19
|
+
'select': 'select',
|
|
20
|
+
'download': 'select',
|
|
21
|
+
'count': 'select',
|
|
22
|
+
'stat': 'select',
|
|
23
|
+
}; */
|
|
24
|
+
class TriggerExecutor {
|
|
25
|
+
counter;
|
|
26
|
+
triggerMap;
|
|
27
|
+
triggerNameMap;
|
|
28
|
+
volatileEntities;
|
|
29
|
+
logger;
|
|
30
|
+
contextBuilder;
|
|
31
|
+
onVolatileTrigger;
|
|
32
|
+
constructor(contextBuilder, logger = console, onVolatileTrigger) {
|
|
33
|
+
this.contextBuilder = contextBuilder;
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.triggerMap = {};
|
|
36
|
+
this.triggerNameMap = {};
|
|
37
|
+
this.volatileEntities = [];
|
|
38
|
+
this.counter = 0;
|
|
39
|
+
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
|
40
|
+
const context = await this.contextBuilder(cxtStr);
|
|
41
|
+
await context.begin();
|
|
42
|
+
try {
|
|
43
|
+
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
|
44
|
+
await context.commit();
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
await context.rollback();
|
|
48
|
+
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
setOnVolatileTrigger(onVolatileTrigger) {
|
|
53
|
+
this.onVolatileTrigger = onVolatileTrigger;
|
|
54
|
+
}
|
|
55
|
+
registerChecker(checker) {
|
|
56
|
+
const { entity, action, type, conditionalFilter, mt } = checker;
|
|
57
|
+
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
|
|
58
|
+
const { fn, when } = (0, checker_1.translateCheckerInAsyncContext)(checker);
|
|
59
|
+
const trigger = {
|
|
60
|
+
checkerType: type,
|
|
61
|
+
name: triggerName,
|
|
62
|
+
priority: checker.priority || Trigger_1.CHECKER_PRIORITY_MAP[type],
|
|
63
|
+
entity,
|
|
64
|
+
action: action,
|
|
65
|
+
fn,
|
|
66
|
+
when,
|
|
67
|
+
mt,
|
|
68
|
+
filter: conditionalFilter,
|
|
69
|
+
};
|
|
70
|
+
this.registerTrigger(trigger);
|
|
71
|
+
}
|
|
72
|
+
/* getCheckers<T extends keyof ED>(entity: T, action: ED[T]['Action'], checkerTypes?: CheckerType[]) {
|
|
73
|
+
const triggers = this.triggerMap[entity] && this.triggerMap[entity]![action]?.filter(
|
|
74
|
+
trigger => (typeof trigger.action === 'string' && trigger.action === action || trigger.action instanceof Array && trigger.action.includes(action as any)
|
|
75
|
+
&& (!checkerTypes || trigger.checkerType && checkerTypes.includes(trigger.checkerType)))
|
|
76
|
+
);
|
|
77
|
+
return triggers;
|
|
78
|
+
} */
|
|
79
|
+
registerTrigger(trigger) {
|
|
80
|
+
// trigger的两种访问方式: by name, by entity/action
|
|
81
|
+
if (this.triggerNameMap.hasOwnProperty(trigger.name)) {
|
|
82
|
+
throw new Error(`不可有同名的触发器「${trigger.name}」`);
|
|
83
|
+
}
|
|
84
|
+
if (typeof trigger.priority !== 'number') {
|
|
85
|
+
trigger.priority = Trigger_1.TRIGGER_DEFAULT_PRIORITY; // 默认值
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
(0, assert_1.default)(trigger.priority <= Trigger_1.CHECKER_MAX_PRIORITY && trigger.priority >= Trigger_1.TRIGGER_MIN_PRIORITY, `trigger「${trigger.name}」的优先级定义越界,应该在${Trigger_1.TRIGGER_MIN_PRIORITY}到${Trigger_1.CHECKER_MAX_PRIORITY}之间`);
|
|
89
|
+
}
|
|
90
|
+
if (trigger.filter) {
|
|
91
|
+
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
|
|
92
|
+
|| trigger.action instanceof Array && !trigger.action.includes('create'), `trigger【${trigger.name}】是create类型但却带有filter`);
|
|
93
|
+
(0, assert_1.default)(trigger.when === 'before' || trigger.when === 'commit', `定义了filter的trigger【${trigger.name}】的when只能是before或者commit`);
|
|
94
|
+
}
|
|
95
|
+
Object.assign(this.triggerNameMap, {
|
|
96
|
+
[trigger.name]: trigger,
|
|
97
|
+
});
|
|
98
|
+
const addTrigger = (action) => {
|
|
99
|
+
const triggers = this.triggerMap[trigger.entity] && this.triggerMap[trigger.entity][action];
|
|
100
|
+
if (triggers) {
|
|
101
|
+
let idx;
|
|
102
|
+
// 这里可以保持有序插入,后面取trigger的时候就不用排序了
|
|
103
|
+
for (idx = 0; idx < triggers.length; idx++) {
|
|
104
|
+
if (triggers[idx].priority > trigger.priority) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
triggers.splice(idx, 0, trigger);
|
|
109
|
+
}
|
|
110
|
+
else if (this.triggerMap[trigger.entity]) {
|
|
111
|
+
Object.assign(this.triggerMap[trigger.entity], {
|
|
112
|
+
[action]: [trigger],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
Object.assign(this.triggerMap, {
|
|
117
|
+
[trigger.entity]: {
|
|
118
|
+
[action]: [trigger],
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
if (typeof trigger.action === 'string') {
|
|
124
|
+
addTrigger(trigger.action);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
trigger.action.forEach(ele => addTrigger(ele));
|
|
128
|
+
}
|
|
129
|
+
if (trigger.when === 'commit' && trigger.strict === 'makeSure') {
|
|
130
|
+
if (this.volatileEntities.indexOf(trigger.entity) === -1) {
|
|
131
|
+
this.volatileEntities.push(trigger.entity);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
unregisterTrigger(trigger) {
|
|
136
|
+
(0, assert_1.default)(trigger.when !== 'commit' || trigger.strict !== 'makeSure', 'could not remove strict volatile triggers');
|
|
137
|
+
const removeTrigger = (action) => {
|
|
138
|
+
const triggers = this.triggerMap[trigger.entity] && this.triggerMap[trigger.entity][action];
|
|
139
|
+
if (triggers) {
|
|
140
|
+
(0, lodash_1.pull)(triggers, trigger);
|
|
141
|
+
(0, lodash_1.unset)(this.triggerNameMap, trigger.name);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
if (typeof trigger.action === 'string') {
|
|
145
|
+
removeTrigger(trigger.action);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
trigger.action.forEach(ele => removeTrigger(ele));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async preCommitTrigger(entity, operation, trigger, context, option) {
|
|
152
|
+
(0, assert_1.default)(trigger.action !== 'select');
|
|
153
|
+
(0, assert_1.default)(trigger.when === 'commit');
|
|
154
|
+
if (trigger.strict === 'makeSure') {
|
|
155
|
+
const uuid = await (0, uuid_1.generateNewIdAsync)();
|
|
156
|
+
const cxtStr = await context.toString();
|
|
157
|
+
const { data } = operation;
|
|
158
|
+
switch (operation.action) {
|
|
159
|
+
case 'create': {
|
|
160
|
+
if (data instanceof Array) {
|
|
161
|
+
data.forEach((d) => {
|
|
162
|
+
if (d.hasOwnProperty(Entity_1.TriggerDataAttribute) || d.hasOwnProperty(Entity_1.TriggerUuidAttribute)) {
|
|
163
|
+
throw new Error('同一行数据上不能同时存在两个跨事务约束');
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
if (data.hasOwnProperty(Entity_1.TriggerDataAttribute) || data.hasOwnProperty(Entity_1.TriggerUuidAttribute)) {
|
|
169
|
+
throw new Error('同一行数据上不能存在两个跨事务约束');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
default: {
|
|
175
|
+
const { filter } = operation;
|
|
176
|
+
// 此时要保证更新或者删除的行上没有跨事务约束
|
|
177
|
+
const filter2 = (0, filter_1.combineFilters)(entity, context.getSchema(), [{
|
|
178
|
+
[Entity_1.TriggerUuidAttribute]: {
|
|
179
|
+
$exists: true,
|
|
180
|
+
},
|
|
181
|
+
}, filter]);
|
|
182
|
+
const count = await context.count(entity, {
|
|
183
|
+
filter: filter2
|
|
184
|
+
}, {});
|
|
185
|
+
if (count > 0) {
|
|
186
|
+
throw new Error(`对象${String(entity)}的行「${JSON.stringify(operation)}」上已经存在未完成的跨事务约束`);
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (data instanceof Array) {
|
|
192
|
+
data.forEach((d) => {
|
|
193
|
+
Object.assign(d, {
|
|
194
|
+
[Entity_1.TriggerDataAttribute]: {
|
|
195
|
+
name: trigger.name,
|
|
196
|
+
cxtStr,
|
|
197
|
+
option,
|
|
198
|
+
},
|
|
199
|
+
[Entity_1.TriggerUuidAttribute]: uuid,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
Object.assign(data, {
|
|
205
|
+
[Entity_1.TriggerDataAttribute]: {
|
|
206
|
+
name: trigger.name,
|
|
207
|
+
cxtStr,
|
|
208
|
+
option,
|
|
209
|
+
},
|
|
210
|
+
[Entity_1.TriggerUuidAttribute]: uuid,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
postCommitTrigger(entity, operation, trigger, context, option) {
|
|
216
|
+
context.on('commit', async () => {
|
|
217
|
+
let ids = [];
|
|
218
|
+
const { opRecords } = context;
|
|
219
|
+
if (operation.action === 'create') {
|
|
220
|
+
const { data } = operation;
|
|
221
|
+
if (data instanceof Array) {
|
|
222
|
+
ids = data.map(ele => ele.id);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
ids = [data.id];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const record = opRecords.find(ele => ele.id === operation.id);
|
|
230
|
+
// 目前框架在operation时,一定会将ids记录在operation当中(见CascadeStore中的doUpdateSingleRowAsync函数
|
|
231
|
+
(0, assert_1.default)(record && record.a !== 'c');
|
|
232
|
+
const { f } = record;
|
|
233
|
+
ids = f.id.$in;
|
|
234
|
+
}
|
|
235
|
+
const cxtStr = await context.toString();
|
|
236
|
+
this.onVolatileTrigger(entity, trigger, ids, cxtStr, option);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
preOperation(entity, operation, context, option) {
|
|
240
|
+
const { action } = operation;
|
|
241
|
+
const triggers = this.triggerMap[entity] && this.triggerMap[entity][action]?.filter(trigger => (typeof trigger.action === 'string' && trigger.action === operation.action
|
|
242
|
+
|| trigger.action instanceof Array && trigger.action.includes(operation.action))
|
|
243
|
+
// 加上modi的过滤条件
|
|
244
|
+
&& this.judgeModiTurn(option, trigger));
|
|
245
|
+
if (triggers) {
|
|
246
|
+
const preTriggers = triggers.filter(ele => ele.when === 'before' && (!ele.check || ele.check(operation)));
|
|
247
|
+
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
248
|
+
(!ele.check || ele.check(operation)));
|
|
249
|
+
if (context instanceof SyncRowStore_1.SyncContext) {
|
|
250
|
+
for (const trigger of preTriggers) {
|
|
251
|
+
if (trigger.filter) {
|
|
252
|
+
// trigger只对满足条件的前项进行判断,如果确定不满足可以pass
|
|
253
|
+
(0, assert_1.default)(operation.action !== 'create');
|
|
254
|
+
const { filter } = trigger;
|
|
255
|
+
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
|
256
|
+
(0, assert_1.default)(!(filterr instanceof Promise));
|
|
257
|
+
const filterRepelled = (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
258
|
+
if (filterRepelled) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const number = trigger.fn({ operation: operation }, context, option);
|
|
263
|
+
if (number > 0) {
|
|
264
|
+
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
(0, assert_1.default)(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// 异步context
|
|
271
|
+
const execPreTrigger = async (idx) => {
|
|
272
|
+
if (idx >= preTriggers.length) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const trigger = preTriggers[idx];
|
|
276
|
+
if (trigger.filter) {
|
|
277
|
+
(0, assert_1.default)(operation.action !== 'create');
|
|
278
|
+
const { filter } = trigger;
|
|
279
|
+
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
280
|
+
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
281
|
+
if (filterRepelled) {
|
|
282
|
+
return execPreTrigger(idx + 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const number = await trigger.fn({ operation: operation }, context, option);
|
|
286
|
+
if (number > 0) {
|
|
287
|
+
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
288
|
+
}
|
|
289
|
+
return execPreTrigger(idx + 1);
|
|
290
|
+
};
|
|
291
|
+
const execCommitTrigger = async (idx) => {
|
|
292
|
+
if (idx >= commitTriggers.length) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const trigger = commitTriggers[idx];
|
|
296
|
+
if (trigger.filter) {
|
|
297
|
+
(0, assert_1.default)(operation.action !== 'create');
|
|
298
|
+
const { filter } = trigger;
|
|
299
|
+
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
300
|
+
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
301
|
+
if (filterRepelled) {
|
|
302
|
+
return execCommitTrigger(idx + 1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
await this.preCommitTrigger(entity, operation, trigger, context, option);
|
|
306
|
+
return execCommitTrigger(idx + 1);
|
|
307
|
+
};
|
|
308
|
+
return execPreTrigger(0)
|
|
309
|
+
.then(() => execCommitTrigger(0));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async execVolatileTrigger(entity, name, ids, context, option) {
|
|
314
|
+
const trigger = this.triggerNameMap[name];
|
|
315
|
+
(0, assert_1.default)(trigger && trigger.when === 'commit');
|
|
316
|
+
(0, assert_1.default)(ids.length > 0);
|
|
317
|
+
const { fn } = trigger;
|
|
318
|
+
await fn({ ids }, context, option);
|
|
319
|
+
if (trigger.strict === 'makeSure') {
|
|
320
|
+
try {
|
|
321
|
+
await context.operate(entity, {
|
|
322
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
323
|
+
action: 'update',
|
|
324
|
+
data: {
|
|
325
|
+
[Entity_1.TriggerDataAttribute]: null,
|
|
326
|
+
[Entity_1.TriggerUuidAttribute]: null,
|
|
327
|
+
},
|
|
328
|
+
filter: {
|
|
329
|
+
id: {
|
|
330
|
+
$in: ids,
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}, { includedDeleted: true, blockTrigger: true });
|
|
334
|
+
}
|
|
335
|
+
catch (err) {
|
|
336
|
+
throw err;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 判断一个trigger和当前modi上下文是否符合
|
|
342
|
+
* trigger的默认行为是:如果是commit时机的trigger,不显式声明则只能在modi apply时执行(create时不执行);非commit时机的trigger,不显式声明则只在modi create时执行
|
|
343
|
+
* @param option
|
|
344
|
+
* @param trigger
|
|
345
|
+
* @returns
|
|
346
|
+
*/
|
|
347
|
+
judgeModiTurn(option, trigger) {
|
|
348
|
+
const { mt, when } = trigger;
|
|
349
|
+
if (option.modiParentEntity) {
|
|
350
|
+
// 在创建modi过程中,标识为apply或者未标识但为commit时执行的trigger默认不能执行
|
|
351
|
+
return mt && ['both', 'create'].includes(mt) || !mt && when !== 'commit';
|
|
352
|
+
}
|
|
353
|
+
else if (option.applyingModi) {
|
|
354
|
+
// 在应用modi过程中,标识为create或者未标识但不为commit时执行的trigger默认不能执行
|
|
355
|
+
return mt && ['both', 'apply'].includes(mt) || !mt && when === 'commit';
|
|
356
|
+
}
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
postOperation(entity, operation, context, option, result) {
|
|
360
|
+
const { action } = operation;
|
|
361
|
+
const triggers = this.triggerMap[entity] && this.triggerMap[entity][action]?.filter(trigger => (typeof trigger.action === 'string' && trigger.action === operation.action
|
|
362
|
+
|| trigger.action instanceof Array && trigger.action.includes(operation.action))
|
|
363
|
+
// 加上modi的过滤条件
|
|
364
|
+
&& this.judgeModiTurn(option, trigger));
|
|
365
|
+
if (triggers) {
|
|
366
|
+
const postTriggers = triggers.filter(ele => ele.when === 'after' && (!ele.check || ele.check(operation)));
|
|
367
|
+
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
368
|
+
(!ele.check || ele.check(operation)));
|
|
369
|
+
if (context instanceof SyncRowStore_1.SyncContext) {
|
|
370
|
+
for (const trigger of postTriggers) {
|
|
371
|
+
const number = trigger.fn({
|
|
372
|
+
operation: operation,
|
|
373
|
+
result: result,
|
|
374
|
+
}, context, option);
|
|
375
|
+
if (number > 0) {
|
|
376
|
+
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// 异步context
|
|
382
|
+
const execPostTrigger = async (idx) => {
|
|
383
|
+
if (idx >= postTriggers.length) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const trigger = postTriggers[idx];
|
|
387
|
+
const number = await trigger.fn({
|
|
388
|
+
operation: operation,
|
|
389
|
+
result: result,
|
|
390
|
+
}, context, option);
|
|
391
|
+
if (number > 0) {
|
|
392
|
+
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
|
393
|
+
}
|
|
394
|
+
return execPostTrigger(idx + 1);
|
|
395
|
+
};
|
|
396
|
+
const execCommitTrigger = async (idx) => {
|
|
397
|
+
if (idx >= commitTriggers.length) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const trigger = commitTriggers[idx];
|
|
401
|
+
if (trigger.filter) {
|
|
402
|
+
(0, assert_1.default)(operation.action !== 'create');
|
|
403
|
+
const { filter } = trigger;
|
|
404
|
+
const filterr = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
405
|
+
const filterRepelled = await (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
|
406
|
+
if (filterRepelled) {
|
|
407
|
+
return execCommitTrigger(idx + 1);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
this.postCommitTrigger(entity, operation, trigger, context, option);
|
|
411
|
+
return execCommitTrigger(idx + 1);
|
|
412
|
+
};
|
|
413
|
+
return execPostTrigger(0)
|
|
414
|
+
.then(() => execCommitTrigger(0));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async checkpoint(timestamp) {
|
|
419
|
+
let result = 0;
|
|
420
|
+
for (const entity of this.volatileEntities) {
|
|
421
|
+
const filter = {
|
|
422
|
+
[Entity_1.TriggerUuidAttribute]: {
|
|
423
|
+
$exists: true,
|
|
424
|
+
},
|
|
425
|
+
[Entity_1.UpdateAtAttribute]: {
|
|
426
|
+
$lt: timestamp,
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const context = await this.contextBuilder();
|
|
430
|
+
if (context.clusterInfo?.usingCluster) {
|
|
431
|
+
const { instanceCount, instanceId } = context.clusterInfo;
|
|
432
|
+
filter.$$seq$$ = {
|
|
433
|
+
$mod: [instanceCount, instanceId],
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
await context.begin();
|
|
437
|
+
try {
|
|
438
|
+
const rows = await context.select(entity, {
|
|
439
|
+
data: {
|
|
440
|
+
id: 1,
|
|
441
|
+
[Entity_1.TriggerDataAttribute]: 1,
|
|
442
|
+
[Entity_1.TriggerUuidAttribute]: 1,
|
|
443
|
+
},
|
|
444
|
+
filter,
|
|
445
|
+
}, {
|
|
446
|
+
includedDeleted: true,
|
|
447
|
+
dontCollect: true,
|
|
448
|
+
forUpdate: 'skip locked', // 防止某个跨事务trigger的逻辑执行周期太长
|
|
449
|
+
});
|
|
450
|
+
const grouped = (0, lodash_1.groupBy)(rows, Entity_1.TriggerUuidAttribute);
|
|
451
|
+
for (const uuid in grouped) {
|
|
452
|
+
const rs = grouped[uuid];
|
|
453
|
+
const { [Entity_1.TriggerDataAttribute]: triggerData } = rs[0];
|
|
454
|
+
const { name, cxtStr, option } = triggerData;
|
|
455
|
+
await context.initialize(JSON.parse(cxtStr), true);
|
|
456
|
+
await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id), context, option);
|
|
457
|
+
}
|
|
458
|
+
await context.commit();
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
await context.rollback();
|
|
462
|
+
this.logger.error(`执行checkpoint时出错,对象是「${entity}」,异常是`, err);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
exports.TriggerExecutor = TriggerExecutor;
|