oak-domain 5.0.19 → 5.1.0
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 +6 -0
- package/lib/base-app-domain/ActionDefDict.js +8 -4
- package/lib/base-app-domain/EntityDict.d.ts +2 -0
- package/lib/base-app-domain/Log/Action.d.ts +12 -0
- package/lib/base-app-domain/Log/Action.js +14 -0
- package/lib/base-app-domain/Log/Schema.d.ts +135 -0
- package/lib/base-app-domain/Log/Schema.js +2 -0
- package/lib/base-app-domain/Log/Storage.d.ts +3 -0
- package/lib/base-app-domain/Log/Storage.js +28 -0
- package/lib/base-app-domain/Log/Style.d.ts +3 -0
- package/lib/base-app-domain/Log/Style.js +15 -0
- package/lib/base-app-domain/ModiEntity/Schema.d.ts +34 -5
- package/lib/base-app-domain/ModiEntity/Storage.js +1 -1
- package/lib/base-app-domain/Oper/Action.d.ts +12 -0
- package/lib/base-app-domain/Oper/Action.js +14 -0
- package/lib/base-app-domain/Oper/Schema.d.ts +56 -6
- package/lib/base-app-domain/Oper/Storage.js +14 -3
- package/lib/base-app-domain/Oper/Style.d.ts +3 -0
- package/lib/base-app-domain/Oper/Style.js +15 -0
- package/lib/base-app-domain/OperEntity/Schema.d.ts +46 -6
- package/lib/base-app-domain/OperEntity/Storage.js +1 -1
- package/lib/base-app-domain/Storage.js +24 -22
- package/lib/base-app-domain/StyleDict.js +8 -4
- package/lib/base-app-domain/User/Schema.d.ts +2 -2
- package/lib/base-app-domain/_SubQuery.d.ts +12 -0
- package/lib/compiler/routerBuilder.js +1 -1
- package/lib/compiler/schemalBuilder.js +22 -1
- package/lib/entities/Log.d.ts +16 -0
- package/lib/entities/Log.js +45 -0
- package/lib/entities/Oper.d.ts +9 -0
- package/lib/entities/Oper.js +33 -3
- package/lib/store/AsyncRowStore.d.ts +3 -2
- package/lib/store/CascadeStore.js +51 -9
- package/lib/store/IntrinsicCheckers.js +7 -2
- package/lib/store/IntrinsicLogics.js +2 -2
- package/lib/store/SyncRowStore.d.ts +3 -2
- package/lib/store/triggers.d.ts +6 -0
- package/lib/store/triggers.js +351 -0
- package/lib/types/AppLoader.d.ts +2 -1
- package/lib/types/Aspect.d.ts +3 -2
- package/lib/types/Auth.d.ts +8 -7
- package/lib/types/Endpoint.d.ts +3 -2
- package/lib/types/Entity.d.ts +1 -0
- package/lib/types/EntityDesc.d.ts +1 -1
- package/lib/types/Exception.d.ts +2 -0
- package/lib/types/Exception.js +5 -1
- package/lib/types/Port.d.ts +3 -2
- package/lib/types/RowStore.d.ts +4 -3
- package/lib/types/Storage.d.ts +1 -0
- package/lib/types/Timer.d.ts +6 -5
- package/lib/types/Trigger.d.ts +21 -20
- package/lib/types/Watcher.d.ts +5 -4
- package/lib/utils/validator.js +4 -2
- package/package.json +1 -1
- package/src/entities/Log.ts +59 -0
- package/src/entities/Oper.ts +45 -4
- package/lib/store/actionAuth.d.ts +0 -4
- package/lib/store/actionAuth.js +0 -25
|
@@ -13,6 +13,7 @@ const AsyncRowStore_1 = require("./AsyncRowStore");
|
|
|
13
13
|
const filter_2 = require("./filter");
|
|
14
14
|
const uuid_1 = require("../utils/uuid");
|
|
15
15
|
const entities_1 = require("../compiler/entities");
|
|
16
|
+
const projection_1 = require("../utils/projection");
|
|
16
17
|
/**这个用来处理级联的select和update,对不同能力的 */
|
|
17
18
|
class CascadeStore extends RowStore_1.RowStore {
|
|
18
19
|
constructor(storageSchema) {
|
|
@@ -676,12 +677,13 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
676
677
|
* @returns
|
|
677
678
|
*/
|
|
678
679
|
destructCascadeUpdate(entity, action, data, context, option, cascadeUpdate, filter, bornAt) {
|
|
679
|
-
const
|
|
680
|
+
const toModi = this.getSchema()[entity].toModi;
|
|
681
|
+
const toLog = this.getSchema()[entity].toLog;
|
|
680
682
|
const option2 = Object.assign({}, option);
|
|
681
683
|
const opData = {};
|
|
682
684
|
const beforeFns = [];
|
|
683
685
|
const afterFns = [];
|
|
684
|
-
if (
|
|
686
|
+
if (toModi && action !== 'remove') {
|
|
685
687
|
// create/update具有modi对象的对象,对其子对象的update行为全部是create modi对象(缓存动作)
|
|
686
688
|
// delete此对象,所有的modi子对象应该通过触发器作废,这个目前先通过系统的trigger来实现
|
|
687
689
|
(0, assert_1.default)(!option2.modiParentId && !option2.modiParentEntity);
|
|
@@ -696,6 +698,24 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
696
698
|
option2.modiParentEntity = entity;
|
|
697
699
|
}
|
|
698
700
|
}
|
|
701
|
+
if (toLog && action !== 'create') {
|
|
702
|
+
// 此对象(及其下辖的cascade更新都需要记录log)
|
|
703
|
+
(0, assert_1.default)(typeof filter.id === 'string');
|
|
704
|
+
(0, assert_1.default)(!option2.logId, 'undo无法支持嵌套');
|
|
705
|
+
const logId = (0, uuid_1.generateNewId)();
|
|
706
|
+
option2.logId = option.logId = logId;
|
|
707
|
+
Object.assign(data, {
|
|
708
|
+
log$entity: [
|
|
709
|
+
{
|
|
710
|
+
id: (0, uuid_1.generateNewId)(),
|
|
711
|
+
data: {
|
|
712
|
+
id: logId,
|
|
713
|
+
},
|
|
714
|
+
action: 'create',
|
|
715
|
+
}
|
|
716
|
+
]
|
|
717
|
+
});
|
|
718
|
+
}
|
|
699
719
|
for (const attr in data) {
|
|
700
720
|
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr, !!bornAt);
|
|
701
721
|
if (relation === 1) {
|
|
@@ -1150,7 +1170,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1150
1170
|
if (!option.dontCollect) {
|
|
1151
1171
|
context.saveOpRecord(entity, operation);
|
|
1152
1172
|
}
|
|
1153
|
-
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity)) {
|
|
1173
|
+
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi', 'log'].includes(entity)) {
|
|
1154
1174
|
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1155
1175
|
(0, assert_1.default)(operId);
|
|
1156
1176
|
const operatorId = context.getCurrentUserId(true);
|
|
@@ -1186,7 +1206,8 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1186
1206
|
id: data instanceof Array ? {
|
|
1187
1207
|
id: { $in: data.map(ele => ele.id) },
|
|
1188
1208
|
} : data.id,
|
|
1189
|
-
}
|
|
1209
|
+
},
|
|
1210
|
+
logId: entity === 'log' ? undefined : option.logId,
|
|
1190
1211
|
},
|
|
1191
1212
|
};
|
|
1192
1213
|
const closeRootMode = context.openRootMode();
|
|
@@ -1206,10 +1227,19 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1206
1227
|
default: {
|
|
1207
1228
|
// 这里要优化一下,显式的对id的update/remove不要去查了,节省数据库层的性能(如果这些row是建立在一个create的modi上也查不到)
|
|
1208
1229
|
const ids = (0, filter_2.getRelevantIds)(filter);
|
|
1209
|
-
|
|
1230
|
+
const undoProjection = action === 'remove' ? (0, projection_1.makeProjection)(entity, this.getSchema()) : (() => {
|
|
1231
|
+
const p = {};
|
|
1232
|
+
Object.keys(data).forEach((attr) => Object.assign(p, {
|
|
1233
|
+
[attr]: 1,
|
|
1234
|
+
}));
|
|
1235
|
+
return p;
|
|
1236
|
+
})();
|
|
1237
|
+
const undoData = {};
|
|
1238
|
+
if (ids.length === 0 || option.logId) {
|
|
1210
1239
|
const selection = {
|
|
1211
1240
|
data: {
|
|
1212
1241
|
id: 1,
|
|
1242
|
+
...undoProjection,
|
|
1213
1243
|
},
|
|
1214
1244
|
filter: operation.filter,
|
|
1215
1245
|
indexFrom: operation.indexFrom,
|
|
@@ -1219,7 +1249,12 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1219
1249
|
dontCollect: true,
|
|
1220
1250
|
forUpdate: true,
|
|
1221
1251
|
});
|
|
1222
|
-
|
|
1252
|
+
rows.forEach((row) => {
|
|
1253
|
+
ids.push(row.id);
|
|
1254
|
+
Object.assign(undoData, {
|
|
1255
|
+
[row.id]: action === 'remove' ? row : (0, lodash_1.omit)(row, [types_1.PrimaryKeyAttribute, Entity_1.CreateAtAttribute]),
|
|
1256
|
+
});
|
|
1257
|
+
});
|
|
1223
1258
|
}
|
|
1224
1259
|
if (data) {
|
|
1225
1260
|
this.preProcessDataUpdated(data);
|
|
@@ -1229,7 +1264,12 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1229
1264
|
// 变成对modi的插入
|
|
1230
1265
|
// 优化,这里如果是对同一个targetEntity反复update,则变成对最后一条create/update的modi进行update,以避免发布文章这样的需求时产生过多的modi
|
|
1231
1266
|
let modiUpsert;
|
|
1232
|
-
if (action
|
|
1267
|
+
if (action === 'update') {
|
|
1268
|
+
// 如果action本身是update,且没有实际update属性,这里可以忽略
|
|
1269
|
+
const updateAttrCount = Object.keys(data).length;
|
|
1270
|
+
if (updateAttrCount === 0) {
|
|
1271
|
+
return {};
|
|
1272
|
+
}
|
|
1233
1273
|
const upsertModis = await this.selectAbjointRowAsync('modi', {
|
|
1234
1274
|
data: {
|
|
1235
1275
|
id: 1,
|
|
@@ -1312,7 +1352,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1312
1352
|
}
|
|
1313
1353
|
else {
|
|
1314
1354
|
const createOper = async () => {
|
|
1315
|
-
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity) && ids.length > 0) {
|
|
1355
|
+
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi', 'log'].includes(entity) && ids.length > 0) {
|
|
1316
1356
|
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1317
1357
|
(0, assert_1.default)(operId);
|
|
1318
1358
|
const operatorId = context.getCurrentUserId(true);
|
|
@@ -1337,7 +1377,9 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1337
1377
|
},
|
|
1338
1378
|
filter: {
|
|
1339
1379
|
id: { $in: ids },
|
|
1340
|
-
}
|
|
1380
|
+
},
|
|
1381
|
+
logId: entity === 'log' ? undefined : option.logId,
|
|
1382
|
+
undoData,
|
|
1341
1383
|
},
|
|
1342
1384
|
};
|
|
1343
1385
|
const closeRootMode = context.openRootMode();
|
|
@@ -329,9 +329,7 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
|
|
|
329
329
|
}
|
|
330
330
|
const condition = attrs.map(ele => matrix[ele]);
|
|
331
331
|
const actions = condition.map(ele => ele.actions).filter(ele => !!ele);
|
|
332
|
-
const filters = condition.map(ele => ele.filter).filter(ele => !!ele);
|
|
333
332
|
const a = actions.length > 0 && (0, lodash_1.intersection)(actions.flat());
|
|
334
|
-
const f = filters.length > 0 && (0, filter_1.combineFilters)(entity, schema, filters);
|
|
335
333
|
if (a) {
|
|
336
334
|
if (!a.includes(action)) {
|
|
337
335
|
// 找到不满足的那个attr
|
|
@@ -339,6 +337,13 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
|
|
|
339
337
|
throw new types_1.OakAttrCantUpdateException(entity, attrsIllegal, `${attrsIllegal.join(',')}不允许被${action}动作更新`);
|
|
340
338
|
}
|
|
341
339
|
}
|
|
340
|
+
const filters = condition.map(ele => {
|
|
341
|
+
if (typeof ele.filter === 'function') {
|
|
342
|
+
return ele.filter(action || 'select');
|
|
343
|
+
}
|
|
344
|
+
return ele.filter;
|
|
345
|
+
}).filter(ele => !!ele);
|
|
346
|
+
const f = filters.length > 0 && (0, filter_1.combineFilters)(entity, schema, filters);
|
|
342
347
|
if (f) {
|
|
343
348
|
const result = (0, filter_1.checkFilterContains)(entity, context, f, filter, true);
|
|
344
349
|
if (result instanceof Promise) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.makeIntrinsicLogics = void 0;
|
|
4
|
-
const
|
|
4
|
+
const triggers_1 = require("./triggers");
|
|
5
5
|
const modi_1 = require("./modi");
|
|
6
6
|
const IntrinsicCheckers_1 = require("./IntrinsicCheckers");
|
|
7
7
|
function createExpiredWatchers(schema) {
|
|
@@ -34,7 +34,7 @@ function createExpiredWatchers(schema) {
|
|
|
34
34
|
function makeIntrinsicLogics(schema, actionDefDict, attrUpdateMatrix) {
|
|
35
35
|
const checkers = (0, IntrinsicCheckers_1.makeIntrinsicCheckers)(schema, actionDefDict, attrUpdateMatrix);
|
|
36
36
|
const triggers = (0, modi_1.createModiRelatedTriggers)(schema);
|
|
37
|
-
triggers.push(...
|
|
37
|
+
triggers.push(...triggers_1.actionAuthTriggers, ...triggers_1.logTriggers);
|
|
38
38
|
const watchers = createExpiredWatchers(schema);
|
|
39
39
|
return {
|
|
40
40
|
triggers,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EntityDict, RowStore, OperateOption, OperationResult, SelectOption, TxnOption, Context, AggregationResult } from "../types";
|
|
2
|
-
|
|
2
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
3
|
+
export declare abstract class SyncContext<ED extends EntityDict & BaseEntityDict> implements Context {
|
|
3
4
|
rowStore: SyncRowStore<ED, this>;
|
|
4
5
|
private uuid?;
|
|
5
6
|
constructor(rowStore: SyncRowStore<ED, SyncContext<ED>>);
|
|
@@ -18,7 +19,7 @@ export declare abstract class SyncContext<ED extends EntityDict> implements Cont
|
|
|
18
19
|
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;
|
|
19
20
|
abstract allowUserUpdate(): boolean;
|
|
20
21
|
}
|
|
21
|
-
export interface SyncRowStore<ED extends EntityDict, Cxt extends SyncContext<ED>> extends RowStore<ED> {
|
|
22
|
+
export interface SyncRowStore<ED extends EntityDict & BaseEntityDict, Cxt extends SyncContext<ED>> extends RowStore<ED> {
|
|
22
23
|
operate<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
|
23
24
|
select<T extends keyof ED, OP extends SelectOption>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
|
24
25
|
count<T extends keyof ED, OP extends SelectOption>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
2
|
+
import { EntityDict } from '../types/Entity';
|
|
3
|
+
import { Trigger } from '../types';
|
|
4
|
+
import { AsyncContext } from './AsyncRowStore';
|
|
5
|
+
export declare const actionAuthTriggers: Trigger<EntityDict & BaseEntityDict, 'actionAuth', AsyncContext<EntityDict & BaseEntityDict>>[];
|
|
6
|
+
export declare const logTriggers: Trigger<EntityDict & BaseEntityDict, 'log', AsyncContext<EntityDict & BaseEntityDict>>[];
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logTriggers = exports.actionAuthTriggers = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const uuid_1 = require("../utils/uuid");
|
|
7
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
8
|
+
const filter_1 = require("./filter");
|
|
9
|
+
exports.actionAuthTriggers = [
|
|
10
|
+
{
|
|
11
|
+
name: '当actionAuth的deActions被置空后,删除此条数据',
|
|
12
|
+
entity: 'actionAuth',
|
|
13
|
+
action: 'update',
|
|
14
|
+
fn: async ({ operation }, context, option) => {
|
|
15
|
+
const { data, filter } = operation;
|
|
16
|
+
if (data.deActions && data.deActions.length === 0) {
|
|
17
|
+
await context.operate('actionAuth', {
|
|
18
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
19
|
+
action: 'remove',
|
|
20
|
+
data: {},
|
|
21
|
+
filter,
|
|
22
|
+
}, option);
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
},
|
|
27
|
+
when: 'after',
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* 回滚某条日志的修改
|
|
32
|
+
* @param log
|
|
33
|
+
* @param context
|
|
34
|
+
*/
|
|
35
|
+
async function undoLog(log, context) {
|
|
36
|
+
const { iState, oper$log } = log;
|
|
37
|
+
for (const oper of oper$log) {
|
|
38
|
+
const { action, targetEntity, filter, undoData, iState } = oper;
|
|
39
|
+
(0, assert_1.default)(iState === 'normal', '非正常状态的oper怎能回滚');
|
|
40
|
+
switch (action) {
|
|
41
|
+
case 'create': {
|
|
42
|
+
await context.operate(targetEntity, {
|
|
43
|
+
id: 'dummy',
|
|
44
|
+
action: 'remove',
|
|
45
|
+
data: {},
|
|
46
|
+
filter: filter,
|
|
47
|
+
}, {
|
|
48
|
+
blockTrigger: true,
|
|
49
|
+
dontCollect: true,
|
|
50
|
+
dontCreateOper: true,
|
|
51
|
+
});
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case 'remove': {
|
|
55
|
+
const ids = (0, filter_1.getRelevantIds)(filter);
|
|
56
|
+
(0, assert_1.default)(ids.length > 0);
|
|
57
|
+
for (const id of ids) {
|
|
58
|
+
await context.operate(targetEntity, {
|
|
59
|
+
id: 'dummy',
|
|
60
|
+
action: 'create',
|
|
61
|
+
data: undoData[id],
|
|
62
|
+
}, {
|
|
63
|
+
blockTrigger: true,
|
|
64
|
+
dontCollect: true,
|
|
65
|
+
dontCreateOper: true,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
default: {
|
|
71
|
+
// 全按update处理
|
|
72
|
+
const ids = (0, filter_1.getRelevantIds)(filter);
|
|
73
|
+
(0, assert_1.default)(ids.length > 0);
|
|
74
|
+
for (const id of ids) {
|
|
75
|
+
await context.operate(targetEntity, {
|
|
76
|
+
id: 'dummy',
|
|
77
|
+
action: 'update',
|
|
78
|
+
data: undoData[id],
|
|
79
|
+
filter: {
|
|
80
|
+
id,
|
|
81
|
+
}
|
|
82
|
+
}, {
|
|
83
|
+
blockTrigger: true,
|
|
84
|
+
dontCollect: true,
|
|
85
|
+
dontCreateOper: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
await context.operate('oper', {
|
|
93
|
+
id: 'dummy',
|
|
94
|
+
action: 'undo',
|
|
95
|
+
data: {
|
|
96
|
+
iState: 'rollbacked',
|
|
97
|
+
},
|
|
98
|
+
filter: {
|
|
99
|
+
id: {
|
|
100
|
+
$in: oper$log.map(ele => ele.id)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}, {
|
|
104
|
+
blockTrigger: true,
|
|
105
|
+
dontCollect: true,
|
|
106
|
+
dontCreateOper: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 重做某条日志的修改
|
|
111
|
+
* @param log
|
|
112
|
+
* @param context
|
|
113
|
+
*/
|
|
114
|
+
async function redoLog(log, context) {
|
|
115
|
+
const { oper$log } = log;
|
|
116
|
+
for (const oper of oper$log) {
|
|
117
|
+
const { action, targetEntity, filter, data, iState } = oper;
|
|
118
|
+
(0, assert_1.default)(iState === 'rollbacked', '非正常状态的oper怎能回滚');
|
|
119
|
+
switch (action) {
|
|
120
|
+
case 'create': {
|
|
121
|
+
await context.operate(targetEntity, {
|
|
122
|
+
id: 'dummy',
|
|
123
|
+
action: 'create',
|
|
124
|
+
data: data,
|
|
125
|
+
}, {
|
|
126
|
+
blockTrigger: true,
|
|
127
|
+
dontCollect: true,
|
|
128
|
+
dontCreateOper: true,
|
|
129
|
+
});
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case 'remove': {
|
|
133
|
+
await context.operate(targetEntity, {
|
|
134
|
+
id: 'dummy',
|
|
135
|
+
action: 'remove',
|
|
136
|
+
data: {},
|
|
137
|
+
filter: filter,
|
|
138
|
+
}, {
|
|
139
|
+
blockTrigger: true,
|
|
140
|
+
dontCollect: true,
|
|
141
|
+
dontCreateOper: true,
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
default: {
|
|
146
|
+
await context.operate(targetEntity, {
|
|
147
|
+
id: 'dummy',
|
|
148
|
+
action,
|
|
149
|
+
data,
|
|
150
|
+
filter: filter,
|
|
151
|
+
}, {
|
|
152
|
+
blockTrigger: true,
|
|
153
|
+
dontCollect: true,
|
|
154
|
+
dontCreateOper: true,
|
|
155
|
+
});
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
await context.operate('oper', {
|
|
161
|
+
id: 'dummy',
|
|
162
|
+
action: 'redo',
|
|
163
|
+
data: {
|
|
164
|
+
iState: 'normal',
|
|
165
|
+
},
|
|
166
|
+
filter: {
|
|
167
|
+
id: {
|
|
168
|
+
$in: oper$log.map(ele => ele.id)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}, {
|
|
172
|
+
blockTrigger: true,
|
|
173
|
+
dontCollect: true,
|
|
174
|
+
dontCreateOper: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
exports.logTriggers = [
|
|
178
|
+
{
|
|
179
|
+
name: '当log被undo时,同一entity上所有在其之后的log全部回滚',
|
|
180
|
+
entity: 'log',
|
|
181
|
+
action: 'undo',
|
|
182
|
+
when: 'after',
|
|
183
|
+
fn: async ({ operation }, context) => {
|
|
184
|
+
const { filter } = operation;
|
|
185
|
+
(0, assert_1.default)(filter);
|
|
186
|
+
const ids = (0, filter_1.getRelevantIds)(filter);
|
|
187
|
+
(0, assert_1.default)(ids.length === 1);
|
|
188
|
+
const [log] = await context.select('log', {
|
|
189
|
+
data: {
|
|
190
|
+
id: 1,
|
|
191
|
+
oper$log: {
|
|
192
|
+
$entity: 'oper',
|
|
193
|
+
data: {
|
|
194
|
+
id: 1,
|
|
195
|
+
undoData: 1,
|
|
196
|
+
action: 1,
|
|
197
|
+
targetEntity: 1,
|
|
198
|
+
filter: 1,
|
|
199
|
+
[types_1.SeqAttribute]: 1,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
entity: 1,
|
|
203
|
+
entityId: 1,
|
|
204
|
+
},
|
|
205
|
+
filter: {
|
|
206
|
+
id: ids[0],
|
|
207
|
+
},
|
|
208
|
+
}, {});
|
|
209
|
+
const logs = await context.select('log', {
|
|
210
|
+
data: {
|
|
211
|
+
id: 1,
|
|
212
|
+
oper$log: {
|
|
213
|
+
$entity: 'oper',
|
|
214
|
+
data: {
|
|
215
|
+
id: 1,
|
|
216
|
+
undoData: 1,
|
|
217
|
+
action: 1,
|
|
218
|
+
targetEntity: 1,
|
|
219
|
+
filter: 1,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
filter: {
|
|
224
|
+
iState: 'normal',
|
|
225
|
+
entity: log.entity,
|
|
226
|
+
entityId: log.entityId,
|
|
227
|
+
[types_1.SeqAttribute]: {
|
|
228
|
+
$gte: log[types_1.SeqAttribute],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
sorter: [
|
|
232
|
+
{
|
|
233
|
+
$attr: {
|
|
234
|
+
[types_1.SeqAttribute]: 1,
|
|
235
|
+
},
|
|
236
|
+
$direction: 'desc',
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
}, { dontCollect: true });
|
|
240
|
+
(0, assert_1.default)(logs.length > 0);
|
|
241
|
+
for (const log2 of logs) {
|
|
242
|
+
await undoLog(log2, context);
|
|
243
|
+
}
|
|
244
|
+
return logs.length;
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: '当log被redo时,同一entity上所有在其之前的log全部重做',
|
|
249
|
+
entity: 'log',
|
|
250
|
+
action: 'redo',
|
|
251
|
+
when: 'after',
|
|
252
|
+
fn: async ({ operation }, context) => {
|
|
253
|
+
const { filter } = operation;
|
|
254
|
+
(0, assert_1.default)(filter);
|
|
255
|
+
const ids = (0, filter_1.getRelevantIds)(filter);
|
|
256
|
+
(0, assert_1.default)(ids.length === 1);
|
|
257
|
+
const [log] = await context.select('log', {
|
|
258
|
+
data: {
|
|
259
|
+
id: 1,
|
|
260
|
+
oper$log: {
|
|
261
|
+
$entity: 'oper',
|
|
262
|
+
data: {
|
|
263
|
+
id: 1,
|
|
264
|
+
data: 1,
|
|
265
|
+
action: 1,
|
|
266
|
+
targetEntity: 1,
|
|
267
|
+
filter: 1,
|
|
268
|
+
[types_1.SeqAttribute]: 1,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
iState: 1,
|
|
272
|
+
entity: 1,
|
|
273
|
+
entityId: 1,
|
|
274
|
+
},
|
|
275
|
+
filter: {
|
|
276
|
+
id: ids[0],
|
|
277
|
+
},
|
|
278
|
+
}, {});
|
|
279
|
+
const logs = await context.select('log', {
|
|
280
|
+
data: {
|
|
281
|
+
id: 1,
|
|
282
|
+
oper$log: {
|
|
283
|
+
$entity: 'oper',
|
|
284
|
+
data: {
|
|
285
|
+
id: 1,
|
|
286
|
+
data: 1,
|
|
287
|
+
action: 1,
|
|
288
|
+
targetEntity: 1,
|
|
289
|
+
filter: 1,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
filter: {
|
|
294
|
+
iState: 'rollbacked',
|
|
295
|
+
entity: log.entity,
|
|
296
|
+
entityId: log.entityId,
|
|
297
|
+
[types_1.SeqAttribute]: {
|
|
298
|
+
$lte: log[types_1.SeqAttribute],
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
sorter: [
|
|
302
|
+
{
|
|
303
|
+
$attr: {
|
|
304
|
+
[types_1.SeqAttribute]: 1,
|
|
305
|
+
},
|
|
306
|
+
$direction: 'asc',
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
}, { dontCollect: true, forUpdate: true });
|
|
310
|
+
(0, assert_1.default)(logs.length > 0);
|
|
311
|
+
for (const log2 of logs) {
|
|
312
|
+
await redoLog(log2, context);
|
|
313
|
+
}
|
|
314
|
+
return logs.length;
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: '当插入新的日志时,过去的rollbacked状态的日志可以删除',
|
|
319
|
+
entity: 'log',
|
|
320
|
+
action: 'create',
|
|
321
|
+
when: 'after',
|
|
322
|
+
fn: async ({ operation }, context, option) => {
|
|
323
|
+
const { data } = operation;
|
|
324
|
+
(0, assert_1.default)(!(data instanceof Array));
|
|
325
|
+
const { entity, entityId } = data;
|
|
326
|
+
const result = await context.operate('oper', {
|
|
327
|
+
id: 'dummy',
|
|
328
|
+
action: 'remove',
|
|
329
|
+
data: {},
|
|
330
|
+
filter: {
|
|
331
|
+
iState: 'rollbacked',
|
|
332
|
+
log: {
|
|
333
|
+
entity: entity,
|
|
334
|
+
entityId: entityId,
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
}, {});
|
|
338
|
+
const result2 = await context.operate('log', {
|
|
339
|
+
id: 'dummy',
|
|
340
|
+
action: 'remove',
|
|
341
|
+
data: {},
|
|
342
|
+
filter: {
|
|
343
|
+
entity: entity,
|
|
344
|
+
entityId: entityId,
|
|
345
|
+
iState: 'rollbacked',
|
|
346
|
+
},
|
|
347
|
+
}, {});
|
|
348
|
+
return (result.oper.remove || 0) + (result2.log.remove || 0);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
];
|
package/lib/types/AppLoader.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { IncomingHttpHeaders } from "http";
|
|
3
3
|
import { AsyncContext, AsyncRowStore } from "../store/AsyncRowStore";
|
|
4
4
|
import { EntityDict, OpRecord } from "./Entity";
|
|
5
|
-
|
|
5
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
6
|
+
export declare abstract class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> {
|
|
6
7
|
protected path: string;
|
|
7
8
|
constructor(path: string);
|
|
8
9
|
abstract execAspect(name: string, header?: IncomingHttpHeaders, contextString?: string, params?: any): Promise<{
|
package/lib/types/Aspect.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { EntityDict } from "./Entity";
|
|
2
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
2
3
|
import { OpRecord } from "./Entity";
|
|
3
4
|
import { AsyncContext } from "../store/AsyncRowStore";
|
|
4
|
-
export interface Aspect<ED extends EntityDict, Cxt extends AsyncContext<ED>> {
|
|
5
|
+
export interface Aspect<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> {
|
|
5
6
|
(params: any, context: Cxt): Promise<any>;
|
|
6
7
|
}
|
|
7
|
-
export interface AspectWrapper<ED extends EntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>> {
|
|
8
|
+
export interface AspectWrapper<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>> {
|
|
8
9
|
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0], ignoreContext?: true) => Promise<{
|
|
9
10
|
result: Awaited<ReturnType<AD[T]>>;
|
|
10
11
|
opRecords?: OpRecord<ED>[];
|
package/lib/types/Auth.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } f
|
|
|
2
2
|
import { AsyncContext } from "../store/AsyncRowStore";
|
|
3
3
|
import { SyncContext } from "../store/SyncRowStore";
|
|
4
4
|
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
|
5
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
5
6
|
import { ModiTurn } from './Trigger';
|
|
6
7
|
export type CheckerType = 'row' | 'data' | 'logical' | 'logicalData';
|
|
7
8
|
/**
|
|
8
9
|
* conditionalFilter是指该action发生时,operation所操作的行中有满足conditionalFilter的行
|
|
9
10
|
* 被转化成trigger的filter条件,详细可看trigger中的说明
|
|
10
11
|
*/
|
|
11
|
-
export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
12
|
+
export type DataChecker<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
12
13
|
priority?: number;
|
|
13
14
|
type: 'data';
|
|
14
15
|
entity: T;
|
|
@@ -16,7 +17,7 @@ export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends A
|
|
|
16
17
|
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
|
17
18
|
checker: (data: Readonly<ED[T]['Create']['data'] | ED[T]['Update']['data']>, context: Cxt) => SyncOrAsync<any>;
|
|
18
19
|
};
|
|
19
|
-
export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
20
|
+
export type RowChecker<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
20
21
|
priority?: number;
|
|
21
22
|
type: 'row';
|
|
22
23
|
entity: T;
|
|
@@ -30,7 +31,7 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
|
|
30
31
|
};
|
|
31
32
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
|
32
33
|
};
|
|
33
|
-
export type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
34
|
+
export type LogicalChecker<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
|
34
35
|
priority?: number;
|
|
35
36
|
type: 'logical' | 'logicalData';
|
|
36
37
|
mt?: ModiTurn;
|
|
@@ -39,19 +40,19 @@ export type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extend
|
|
|
39
40
|
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<any>;
|
|
40
41
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
|
41
42
|
};
|
|
42
|
-
export type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt>;
|
|
43
|
-
export type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
|
43
|
+
export type Checker<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt>;
|
|
44
|
+
export type AuthDef<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
|
|
44
45
|
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
|
45
46
|
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
|
46
47
|
cascadeRemove?: {
|
|
47
48
|
[E in (keyof ED | keyof ED[T]['Schema'] | '@entity')]?: ActionOnRemove;
|
|
48
49
|
};
|
|
49
50
|
};
|
|
50
|
-
export type CascadeRemoveDefDict<ED extends EntityDict> = {
|
|
51
|
+
export type CascadeRemoveDefDict<ED extends EntityDict & BaseEntityDict> = {
|
|
51
52
|
[T in keyof ED]?: {
|
|
52
53
|
[E in (keyof ED | keyof ED[T]['Schema'] | '@entity')]?: ActionOnRemove;
|
|
53
54
|
};
|
|
54
55
|
};
|
|
55
|
-
export type AuthDefDict<ED extends EntityDict> = {
|
|
56
|
+
export type AuthDefDict<ED extends EntityDict & BaseEntityDict> = {
|
|
56
57
|
[K in keyof ED]?: AuthDef<ED, K>;
|
|
57
58
|
};
|
package/lib/types/Endpoint.d.ts
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import { IncomingHttpHeaders, IncomingMessage } from "http";
|
|
3
3
|
import { AsyncContext } from "../store/AsyncRowStore";
|
|
4
4
|
import { EntityDict } from "./Entity";
|
|
5
|
-
|
|
5
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
6
|
+
export interface EndpointItem<ED extends EntityDict & BaseEntityDict, BackCxt extends AsyncContext<ED>> {
|
|
6
7
|
name: string;
|
|
7
8
|
params?: string[];
|
|
8
9
|
method: 'get' | 'post' | 'put' | 'delete';
|
|
9
10
|
fn: (context: BackCxt, params: Record<string, string>, headers: IncomingHttpHeaders, req: IncomingMessage, body?: any) => Promise<any>;
|
|
10
11
|
}
|
|
11
|
-
export type Endpoint<ED extends EntityDict, BackCxt extends AsyncContext<ED>> = EndpointItem<ED, BackCxt> | EndpointItem<ED, BackCxt>[];
|
|
12
|
+
export type Endpoint<ED extends EntityDict & BaseEntityDict, BackCxt extends AsyncContext<ED>> = EndpointItem<ED, BackCxt> | EndpointItem<ED, BackCxt>[];
|