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.
Files changed (58) hide show
  1. package/lib/base-app-domain/ActionDefDict.d.ts +6 -0
  2. package/lib/base-app-domain/ActionDefDict.js +8 -4
  3. package/lib/base-app-domain/EntityDict.d.ts +2 -0
  4. package/lib/base-app-domain/Log/Action.d.ts +12 -0
  5. package/lib/base-app-domain/Log/Action.js +14 -0
  6. package/lib/base-app-domain/Log/Schema.d.ts +135 -0
  7. package/lib/base-app-domain/Log/Schema.js +2 -0
  8. package/lib/base-app-domain/Log/Storage.d.ts +3 -0
  9. package/lib/base-app-domain/Log/Storage.js +28 -0
  10. package/lib/base-app-domain/Log/Style.d.ts +3 -0
  11. package/lib/base-app-domain/Log/Style.js +15 -0
  12. package/lib/base-app-domain/ModiEntity/Schema.d.ts +34 -5
  13. package/lib/base-app-domain/ModiEntity/Storage.js +1 -1
  14. package/lib/base-app-domain/Oper/Action.d.ts +12 -0
  15. package/lib/base-app-domain/Oper/Action.js +14 -0
  16. package/lib/base-app-domain/Oper/Schema.d.ts +56 -6
  17. package/lib/base-app-domain/Oper/Storage.js +14 -3
  18. package/lib/base-app-domain/Oper/Style.d.ts +3 -0
  19. package/lib/base-app-domain/Oper/Style.js +15 -0
  20. package/lib/base-app-domain/OperEntity/Schema.d.ts +46 -6
  21. package/lib/base-app-domain/OperEntity/Storage.js +1 -1
  22. package/lib/base-app-domain/Storage.js +24 -22
  23. package/lib/base-app-domain/StyleDict.js +8 -4
  24. package/lib/base-app-domain/User/Schema.d.ts +2 -2
  25. package/lib/base-app-domain/_SubQuery.d.ts +12 -0
  26. package/lib/compiler/routerBuilder.js +1 -1
  27. package/lib/compiler/schemalBuilder.js +22 -1
  28. package/lib/entities/Log.d.ts +16 -0
  29. package/lib/entities/Log.js +45 -0
  30. package/lib/entities/Oper.d.ts +9 -0
  31. package/lib/entities/Oper.js +33 -3
  32. package/lib/store/AsyncRowStore.d.ts +3 -2
  33. package/lib/store/CascadeStore.js +51 -9
  34. package/lib/store/IntrinsicCheckers.js +7 -2
  35. package/lib/store/IntrinsicLogics.js +2 -2
  36. package/lib/store/SyncRowStore.d.ts +3 -2
  37. package/lib/store/triggers.d.ts +6 -0
  38. package/lib/store/triggers.js +351 -0
  39. package/lib/types/AppLoader.d.ts +2 -1
  40. package/lib/types/Aspect.d.ts +3 -2
  41. package/lib/types/Auth.d.ts +8 -7
  42. package/lib/types/Endpoint.d.ts +3 -2
  43. package/lib/types/Entity.d.ts +1 -0
  44. package/lib/types/EntityDesc.d.ts +1 -1
  45. package/lib/types/Exception.d.ts +2 -0
  46. package/lib/types/Exception.js +5 -1
  47. package/lib/types/Port.d.ts +3 -2
  48. package/lib/types/RowStore.d.ts +4 -3
  49. package/lib/types/Storage.d.ts +1 -0
  50. package/lib/types/Timer.d.ts +6 -5
  51. package/lib/types/Trigger.d.ts +21 -20
  52. package/lib/types/Watcher.d.ts +5 -4
  53. package/lib/utils/validator.js +4 -2
  54. package/package.json +1 -1
  55. package/src/entities/Log.ts +59 -0
  56. package/src/entities/Oper.ts +45 -4
  57. package/lib/store/actionAuth.d.ts +0 -4
  58. 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 modiAttr = this.getSchema()[entity].toModi;
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 (modiAttr && action !== 'remove') {
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
- if (ids.length === 0) {
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
- ids.push(...(rows.map(ele => ele.id)));
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 !== 'remove') {
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 actionAuth_1 = require("./actionAuth");
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(...actionAuth_1.triggers);
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
- export declare abstract class SyncContext<ED extends EntityDict> implements Context {
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
+ ];
@@ -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
- export declare abstract class AppLoader<ED extends EntityDict, Cxt extends AsyncContext<ED>> {
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<{
@@ -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>[];
@@ -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
  };
@@ -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
- export interface EndpointItem<ED extends EntityDict, BackCxt extends AsyncContext<ED>> {
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>[];
@@ -39,6 +39,7 @@ export type OperateOption = {
39
39
  allowExists?: boolean;
40
40
  modiParentId?: string;
41
41
  modiParentEntity?: string;
42
+ logId?: string;
42
43
  deletePhysically?: boolean;
43
44
  applyingModi?: boolean;
44
45
  dummy?: 1;