oak-domain 5.1.14 → 5.1.16

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.
@@ -1132,36 +1132,37 @@ class CascadeStore extends RowStore_1.RowStore {
1132
1132
  }
1133
1133
  // 说明没有已有的modi,必须要创建新的,此时应当在option中有相应的parentEntity/Id
1134
1134
  const { modiParentEntity, modiParentId } = option;
1135
- (0, assert_1.default)(modiParentEntity && modiParentId);
1136
- return {
1137
- id: 'dummy',
1138
- action: 'create',
1139
- data: {
1140
- id: operId,
1141
- targetEntity: entity,
1142
- entity: modiParentEntity,
1143
- entityId: modiParentId,
1144
- action,
1145
- data,
1146
- iState: 'active',
1147
- filter: ids.length > 0 ? {
1148
- id: {
1149
- $in: ids,
1135
+ if (modiParentEntity && modiParentId) {
1136
+ return {
1137
+ id: 'dummy',
1138
+ action: 'create',
1139
+ data: {
1140
+ id: operId,
1141
+ targetEntity: entity,
1142
+ entity: modiParentEntity,
1143
+ entityId: modiParentId,
1144
+ action,
1145
+ data,
1146
+ iState: 'active',
1147
+ filter: ids.length > 0 ? {
1148
+ id: {
1149
+ $in: ids,
1150
+ },
1151
+ } : {
1152
+ id: ids[0],
1150
1153
  },
1151
- } : {
1152
- id: ids[0],
1154
+ modiEntity$modi: {
1155
+ id: 'dummy',
1156
+ action: 'create',
1157
+ data: await Promise.all(ids.map(async (id) => ({
1158
+ id: await (0, uuid_1.generateNewIdAsync)(),
1159
+ entity: entity,
1160
+ entityId: id,
1161
+ }))),
1162
+ }
1153
1163
  },
1154
- modiEntity$modi: {
1155
- id: 'dummy',
1156
- action: 'create',
1157
- data: await Promise.all(ids.map(async (id) => ({
1158
- id: await (0, uuid_1.generateNewIdAsync)(),
1159
- entity: entity,
1160
- entityId: id,
1161
- }))),
1162
- }
1163
- },
1164
- };
1164
+ };
1165
+ }
1165
1166
  }
1166
1167
  /**
1167
1168
  * 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
@@ -1396,25 +1397,37 @@ class CascadeStore extends RowStore_1.RowStore {
1396
1397
  }
1397
1398
  const createModi = async () => {
1398
1399
  const modiOperation = await this.tryMergeModi(entity, operation, context, ids, option);
1399
- const closeRootMode = context.openRootMode();
1400
- await this.cascadeUpdateAsync('modi', modiOperation, context, option);
1401
- closeRootMode();
1402
- return {
1403
- modi: {
1404
- ['create']: 1,
1405
- },
1406
- };
1400
+ if (modiOperation) {
1401
+ const closeRootMode = context.openRootMode();
1402
+ await this.cascadeUpdateAsync('modi', modiOperation, context, option);
1403
+ closeRootMode();
1404
+ return {
1405
+ modi: {
1406
+ ['create']: 1,
1407
+ },
1408
+ };
1409
+ }
1410
+ console.warn(`destination includes modies can not be found here: [${entity}], [${JSON.stringify(operation)}]`);
1411
+ return {};
1407
1412
  };
1408
- if (option.modiParentEntity && !['modi', 'modiEntity'].includes(entity)) {
1409
- // 延时更新,变成对modi的操作
1410
- if (action === 'update' && Object.keys(data).length === 0) {
1411
- return {};
1413
+ const saveRecordAndCreateOper = async () => {
1414
+ if (action === 'remove') {
1415
+ if (!option.dontCollect) {
1416
+ context.saveOpRecord(entity, {
1417
+ id: operId,
1418
+ action,
1419
+ data: data,
1420
+ filter: {
1421
+ id: {
1422
+ $in: ids,
1423
+ }
1424
+ },
1425
+ });
1426
+ }
1412
1427
  }
1413
- return createModi();
1414
- }
1415
- else {
1416
- const saveRecordAndCreateOper = async () => {
1417
- if (action === 'remove') {
1428
+ else {
1429
+ const updateAttrCount = Object.keys(data).length;
1430
+ if (updateAttrCount > 0) {
1418
1431
  if (!option.dontCollect) {
1419
1432
  context.saveOpRecord(entity, {
1420
1433
  id: operId,
@@ -1428,68 +1441,61 @@ class CascadeStore extends RowStore_1.RowStore {
1428
1441
  });
1429
1442
  }
1430
1443
  }
1431
- else {
1432
- const updateAttrCount = Object.keys(data).length;
1433
- if (updateAttrCount > 0) {
1434
- if (!option.dontCollect) {
1435
- context.saveOpRecord(entity, {
1436
- id: operId,
1437
- action,
1438
- data: data,
1439
- filter: {
1440
- id: {
1441
- $in: ids,
1442
- }
1443
- },
1444
- });
1445
- }
1446
- }
1447
- else {
1448
- return {};
1449
- }
1450
- }
1451
- if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi', 'log'].includes(entity) && ids.length > 0) {
1452
- // 按照框架要求生成Oper和OperEntity这两个内置的对象
1453
- (0, assert_1.default)(operId);
1454
- const operatorId = context.getCurrentUserId(true);
1455
- const createOper = {
1456
- id: 'dummy',
1457
- action: 'create',
1458
- data: {
1459
- id: operId,
1460
- action,
1461
- data,
1462
- targetEntity: entity,
1463
- bornAt,
1464
- operatorId,
1465
- operEntity$oper: {
1466
- id: 'dummy',
1467
- action: 'create',
1468
- data: await Promise.all(ids.map(async (ele) => ({
1469
- id: await (0, uuid_1.generateNewIdAsync)(),
1470
- entityId: ele,
1471
- entity: entity,
1472
- })))
1473
- },
1474
- filter: {
1475
- id: { $in: ids },
1476
- },
1477
- logId: entity === 'log' ? undefined : option.logId,
1478
- undoData,
1444
+ }
1445
+ if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi', 'log'].includes(entity) && ids.length > 0) {
1446
+ // 按照框架要求生成Oper和OperEntity这两个内置的对象
1447
+ (0, assert_1.default)(operId);
1448
+ const operatorId = context.getCurrentUserId(true);
1449
+ const createOper = {
1450
+ id: 'dummy',
1451
+ action: 'create',
1452
+ data: {
1453
+ id: operId,
1454
+ action,
1455
+ data,
1456
+ targetEntity: entity,
1457
+ bornAt,
1458
+ operatorId,
1459
+ operEntity$oper: {
1460
+ id: 'dummy',
1461
+ action: 'create',
1462
+ data: await Promise.all(ids.map(async (ele) => ({
1463
+ id: await (0, uuid_1.generateNewIdAsync)(),
1464
+ entityId: ele,
1465
+ entity: entity,
1466
+ })))
1479
1467
  },
1480
- };
1481
- const closeRootMode = context.openRootMode();
1482
- await this.cascadeUpdateAsync('oper', createOper, context, {
1483
- dontCollect: !option.logId, // 如果是在创建log,则把oper收集回去
1484
- });
1485
- closeRootMode();
1486
- }
1487
- return {
1488
- [entity]: {
1489
- [action]: ids.length,
1490
- }
1468
+ filter: {
1469
+ id: { $in: ids },
1470
+ },
1471
+ logId: entity === 'log' ? undefined : option.logId,
1472
+ undoData,
1473
+ },
1491
1474
  };
1475
+ const closeRootMode = context.openRootMode();
1476
+ await this.cascadeUpdateAsync('oper', createOper, context, {
1477
+ dontCollect: !option.logId, // 如果是在创建log,则把oper收集回去
1478
+ });
1479
+ closeRootMode();
1480
+ }
1481
+ return {
1482
+ [entity]: {
1483
+ [action]: ids.length,
1484
+ }
1492
1485
  };
1486
+ };
1487
+ if (action !== 'remove' && Object.keys(data).length === 0) {
1488
+ if (action !== 'update') {
1489
+ // 如果不是update,这里还是记一条oper
1490
+ return saveRecordAndCreateOper();
1491
+ }
1492
+ return {};
1493
+ }
1494
+ if (option.modiParentEntity && !['modi', 'modiEntity'].includes(entity)) {
1495
+ // 延时更新,变成对modi的操作
1496
+ return createModi();
1497
+ }
1498
+ else {
1493
1499
  const count = await this.updateAbjointRowAsync(entity, operation, context, option);
1494
1500
  if (count === ids.length) {
1495
1501
  return await saveRecordAndCreateOper();
@@ -352,11 +352,11 @@ class TriggerExecutor {
352
352
  const trigger = this.triggerNameMap[name];
353
353
  (0, assert_1.default)(trigger && trigger.when === 'commit');
354
354
  // assert(ids.length > 0);
355
- const { fn } = trigger;
355
+ const { fn, cleanTriggerDataBySelf } = trigger;
356
356
  const closeRoot = trigger.asRoot && context.openRootMode();
357
357
  try {
358
358
  const callback = await fn({ ids }, context, option);
359
- if (trigger.strict === 'makeSure' && ids.length) {
359
+ if (trigger.strict === 'makeSure' && ids.length && !cleanTriggerDataBySelf) {
360
360
  // 这里开root模式,否则还可能有权限问题
361
361
  const closeRoot2 = context.openRootMode();
362
362
  try {
@@ -485,67 +485,10 @@ class TriggerExecutor {
485
485
  }
486
486
  async checkpoint(timestamp) {
487
487
  let result = 0;
488
- for (const entity of this.volatileEntities) {
489
- const filter = {
490
- [Entity_1.TriggerUuidAttribute]: {
491
- $exists: true,
492
- },
493
- [Entity_1.UpdateAtAttribute]: {
494
- $lt: timestamp,
495
- }
496
- };
497
- const context = this.contextBuilder();
498
- await context.begin();
499
- try {
500
- const rows = await context.select(entity, {
501
- data: {
502
- id: 1,
503
- },
504
- filter,
505
- }, {
506
- includedDeleted: true,
507
- dontCollect: true,
508
- });
509
- if (rows.length > 0) {
510
- // 要用id来再锁一次,不然会锁住filter的范围,影响并发性
511
- // by Xc 20240314,在haina-busi和haina-cn数据sync过程中发现这个问题
512
- const rows2 = await context.select(entity, {
513
- data: {
514
- id: 1,
515
- [Entity_1.TriggerDataAttribute]: 1,
516
- [Entity_1.TriggerUuidAttribute]: 1,
517
- },
518
- filter: {
519
- id: {
520
- $in: rows.map(ele => ele.id),
521
- },
522
- },
523
- }, {
524
- includedDeleted: true,
525
- dontCollect: true,
526
- forUpdate: 'skip locked', // 如果加不上锁就下次再处理,或者有可能应用自己在处理
527
- });
528
- const grouped = (0, lodash_1.groupBy)(rows2, Entity_1.TriggerUuidAttribute);
529
- for (const uuid in grouped) {
530
- const rs = grouped[uuid];
531
- const { [Entity_1.TriggerDataAttribute]: triggerData } = rs[0];
532
- const { name, cxtStr, option } = triggerData;
533
- await context.initialize(JSON.parse(cxtStr), true);
534
- await this.execVolatileTrigger(entity, name, rs.map(ele => ele.id), context, option);
535
- }
536
- }
537
- await context.commit();
538
- result += rows.length;
539
- }
540
- catch (err) {
541
- if (!(err instanceof types_1.OakPartialSuccess)) {
542
- await context.rollback();
543
- this.logger.error(`error in checkpoint on entity 「${entity}」`, err);
544
- }
545
- else {
546
- await context.commit();
547
- this.logger.error(`error in checkpoint on entity 「${entity}」`, err);
548
- }
488
+ for (const name in this.triggerNameMap) {
489
+ const trigger = this.triggerNameMap[name];
490
+ if (trigger && trigger.when === 'commit') {
491
+ result += await this.independentCheckPoint(name, timestamp);
549
492
  }
550
493
  }
551
494
  return result;
@@ -44,7 +44,7 @@ function translateCheckerInAsyncContext(checker, schema) {
44
44
  };
45
45
  }
46
46
  case 'row': {
47
- const { filter, errMsg, inconsistentRows } = checker;
47
+ const { filter, errMsg, err, inconsistentRows } = checker;
48
48
  const fn = (async ({ operation }, context, option) => {
49
49
  const { filter: operationFilter, data, action, bornAt } = operation;
50
50
  const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
@@ -66,7 +66,7 @@ function translateCheckerInAsyncContext(checker, schema) {
66
66
  dontCollect: true,
67
67
  blockTrigger: true,
68
68
  });
69
- const e = new Exception_1.OakRowInconsistencyException(errMsg);
69
+ const e = new (err || (Exception_1.OakRowInconsistencyException))(errMsg);
70
70
  e.addData(entity2, rows2, context.getSchema());
71
71
  throw e;
72
72
  }
@@ -81,7 +81,7 @@ function translateCheckerInAsyncContext(checker, schema) {
81
81
  dontCollect: true,
82
82
  blockTrigger: true,
83
83
  }); */
84
- const e = new Exception_1.OakRowInconsistencyException(errMsg);
84
+ const e = new (err || (Exception_1.OakRowInconsistencyException))(errMsg);
85
85
  // e.addData(entity, rows2);
86
86
  throw e;
87
87
  }
package/lib/store/modi.js CHANGED
@@ -73,42 +73,79 @@ function createModiRelatedCheckers(schema) {
73
73
  continue;
74
74
  }
75
75
  const restActions = (0, lodash_1.difference)(actions, action_1.appendOnlyActions);
76
- checkers.push({
77
- entity,
78
- action: restActions,
79
- type: 'row',
80
- filter: (operation, context, option) => {
81
- /**
82
- * 只有一种情况可以通过,即当前是在更新和active的modi所指向同一个父更新对象。
83
- * 比如:先申请了一个公司(company),再申请修改公司(companyApplyment),这时所有的active modi都指向此条companyApplyment
84
- * 这时:
85
- * 1)再申请一条新的修改公司(create companyApplyment),应被拒绝
86
- * 2)申请修改原来的companyApplyment(update companyApplyment),可以通过
87
- * 3)在其它路径上对此company对象进行直接的更新,应被拒绝
88
- */
89
- if (option.modiParentEntity) {
90
- const { modiParentEntity, modiParentId } = option;
91
- (0, assert_1.default)(modiParentEntity);
92
- (0, assert_1.default)(modiParentId);
76
+ if (!process.env.OAK_DISABLE_MODI_LOCK) {
77
+ checkers.push({
78
+ entity,
79
+ action: restActions,
80
+ type: 'row',
81
+ filter: (operation, context, option) => {
82
+ /**
83
+ * 只有一种情况可以通过,即当前是在更新和activemodi所指向同一个父更新对象。
84
+ * 比如:先申请了一个公司(company),再申请修改公司(companyApplyment),这时所有的active modi都指向此条companyApplyment
85
+ * 这时:
86
+ * 1)再申请一条新的修改公司(create companyApplyment),应被拒绝
87
+ * 2)申请修改原来的companyApplyment(update companyApplyment),可以通过
88
+ * 3)在其它路径上对此company对象进行直接的更新,应被拒绝
89
+ */
90
+ if (option.modiParentEntity) {
91
+ const { modiParentEntity, modiParentId } = option;
92
+ (0, assert_1.default)(modiParentEntity);
93
+ (0, assert_1.default)(modiParentId);
94
+ return {
95
+ modiEntity$entity: {
96
+ '#sqp': 'not in',
97
+ entity,
98
+ modi: {
99
+ iState: 'active',
100
+ $or: [
101
+ {
102
+ entity: {
103
+ $ne: modiParentEntity,
104
+ },
105
+ },
106
+ {
107
+ entityId: {
108
+ $ne: modiParentId,
109
+ },
110
+ }
111
+ ],
112
+ },
113
+ },
114
+ /* id: {
115
+ $nin: {
116
+ entity: 'modiEntity',
117
+ data: {
118
+ entityId: 1,
119
+ },
120
+ filter: {
121
+ entity,
122
+ modi: {
123
+ iState: 'active',
124
+ $or: [
125
+ {
126
+ entity: {
127
+ $ne: modiParentEntity,
128
+ },
129
+ },
130
+ {
131
+ entityId: {
132
+ $ne: modiParentId,
133
+ },
134
+ }
135
+ ],
136
+ },
137
+ },
138
+ },
139
+ } */
140
+ };
141
+ }
93
142
  return {
94
143
  modiEntity$entity: {
95
144
  '#sqp': 'not in',
96
145
  entity,
97
146
  modi: {
98
147
  iState: 'active',
99
- $or: [
100
- {
101
- entity: {
102
- $ne: modiParentEntity,
103
- },
104
- },
105
- {
106
- entityId: {
107
- $ne: modiParentId,
108
- },
109
- }
110
- ],
111
- },
148
+ }
112
149
  },
113
150
  /* id: {
114
151
  $nin: {
@@ -120,50 +157,15 @@ function createModiRelatedCheckers(schema) {
120
157
  entity,
121
158
  modi: {
122
159
  iState: 'active',
123
- $or: [
124
- {
125
- entity: {
126
- $ne: modiParentEntity,
127
- },
128
- },
129
- {
130
- entityId: {
131
- $ne: modiParentId,
132
- },
133
- }
134
- ],
135
- },
160
+ }
136
161
  },
137
162
  },
138
163
  } */
139
164
  };
140
- }
141
- return {
142
- modiEntity$entity: {
143
- '#sqp': 'not in',
144
- entity,
145
- modi: {
146
- iState: 'active',
147
- }
148
- },
149
- /* id: {
150
- $nin: {
151
- entity: 'modiEntity',
152
- data: {
153
- entityId: 1,
154
- },
155
- filter: {
156
- entity,
157
- modi: {
158
- iState: 'active',
159
- }
160
- },
161
- },
162
- } */
163
- };
164
- },
165
- errMsg: '您请求的更新对象上还有正在申请的更新,请等该更新结束后再试',
166
- });
165
+ },
166
+ errMsg: '您请求的更新对象上还有正在申请的更新,请等该更新结束后再试',
167
+ });
168
+ }
167
169
  }
168
170
  return checkers;
169
171
  }
@@ -1,4 +1,4 @@
1
- import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } from ".";
1
+ import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove, SyncOrAsync, OakException } from ".";
2
2
  import { AsyncContext } from "../store/AsyncRowStore";
3
3
  import { SyncContext } from "../store/SyncRowStore";
4
4
  import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
@@ -25,6 +25,7 @@ export type RowChecker<ED extends EntityDict & BaseEntityDict, T extends keyof E
25
25
  action: ED[T]['Action'] | Array<ED[T]['Action']>;
26
26
  filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>);
27
27
  errMsg?: string;
28
+ err?: new (msg?: string) => OakException<ED>;
28
29
  inconsistentRows?: {
29
30
  entity: keyof ED;
30
31
  selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
@@ -40,6 +40,7 @@ interface TriggerCrossTxn<ED extends EntityDict & BaseEntityDict, Cxt extends As
40
40
  when: 'commit';
41
41
  strict?: 'takeEasy' | 'makeSure';
42
42
  cs?: true;
43
+ cleanTriggerDataBySelf?: true;
43
44
  singleton?: true;
44
45
  grouped?: true;
45
46
  fn: (event: {
@@ -78,8 +78,7 @@ class SimpleConnector {
78
78
  ;
79
79
  async parseAspectResult(response) {
80
80
  if (response.status > 299) {
81
- const err = new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
82
- throw this.makeException(err);
81
+ throw new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
83
82
  }
84
83
  const message = response.headers.get('oak-message');
85
84
  const responseType = response.headers.get('Content-Type') ||
@@ -120,14 +119,10 @@ class SimpleConnector {
120
119
  }
121
120
  catch (err) {
122
121
  // fetch返回异常一定是网络异常
123
- let exception = err;
124
122
  if (err instanceof types_1.OakRequestTimeoutException) {
125
- exception = new types_1.OakNetworkException(`接口请求超时`);
123
+ throw new types_1.OakNetworkException(`接口请求超时`);
126
124
  }
127
- else {
128
- exception = new types_1.OakNetworkException(`接口请求时发生网络异常`);
129
- }
130
- throw this.makeException(exception);
125
+ throw new types_1.OakNetworkException(`接口请求时发生网络异常`);
131
126
  }
132
127
  return this.parseAspectResult(response);
133
128
  }
@@ -149,18 +144,13 @@ class SimpleConnector {
149
144
  response = await this.fetchWithTimeout(this.serverSubscribePointUrl, {}, this.timeout);
150
145
  }
151
146
  catch (err) {
152
- let exception = err;
153
147
  if (err instanceof types_1.OakRequestTimeoutException) {
154
- exception = new types_1.OakNetworkException(`接口请求超时`);
155
- }
156
- else {
157
- exception = new types_1.OakNetworkException(`接口请求时发生网络异常`);
148
+ throw new types_1.OakNetworkException(`接口请求超时`);
158
149
  }
159
- throw this.makeException(exception);
150
+ throw new types_1.OakNetworkException(`接口请求时发生网络异常`);
160
151
  }
161
152
  if (response.status > 299) {
162
- const err = new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
163
- throw this.makeException(err);
153
+ throw new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
164
154
  }
165
155
  const message = response.headers.get('oak-message');
166
156
  const responseType = response.headers.get('Content-Type') ||
@@ -1,6 +1,14 @@
1
1
  import { EntityDict } from '../types/Entity';
2
2
  import { EntityDict as BaseEntityDict } from '../base-app-domain';
3
3
  import { StorageSchema } from '../types';
4
+ /**
5
+ * 比较两行是否完全相等
6
+ * @param schema
7
+ * @param entity
8
+ * @param row1
9
+ * @param row2
10
+ * @returns 相等返回true,否则返回false
11
+ */
4
12
  export declare function compareRow<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(schema: StorageSchema<ED>, entity: T, row1: Partial<ED[T]['Schema']>, row2: Partial<ED[T]['Schema']>): boolean;
5
13
  /**
6
14
  * 比较两行数据是否完全相等
package/lib/utils/row.js CHANGED
@@ -5,6 +5,14 @@ const tslib_1 = require("tslib");
5
5
  const lodash_1 = require("./lodash");
6
6
  const relation_1 = require("../store/relation");
7
7
  const assert_1 = tslib_1.__importDefault(require("assert"));
8
+ /**
9
+ * 比较两行是否完全相等
10
+ * @param schema
11
+ * @param entity
12
+ * @param row1
13
+ * @param row2
14
+ * @returns 相等返回true,否则返回false
15
+ */
8
16
  function compareRow(schema, entity, row1, row2) {
9
17
  const attrs1 = Object.keys(row1 || {}).filter(ele => !ele.startsWith('$$'));
10
18
  const attrs2 = Object.keys(row2 || {}).filter(ele => !ele.startsWith('$$'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oak-domain",
3
- "version": "5.1.14",
3
+ "version": "5.1.16",
4
4
  "author": {
5
5
  "name": "XuChang"
6
6
  },