oak-domain 5.1.1 → 5.1.2
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 +1 -3
- package/lib/base-app-domain/Log/Action.d.ts +1 -7
- package/lib/base-app-domain/Log/Action.js +2 -11
- package/lib/base-app-domain/Log/Schema.d.ts +4 -4
- package/lib/base-app-domain/Log/Storage.js +1 -0
- package/lib/base-app-domain/Log/Style.js +0 -6
- package/lib/entities/Log.d.ts +2 -7
- package/lib/entities/Log.js +1 -14
- package/lib/store/CascadeStore.js +19 -10
- package/lib/store/IntrinsicCheckers.js +4 -4
- package/lib/store/triggers.js +35 -21
- package/lib/types/Connector.d.ts +3 -1
- package/lib/types/Sync.d.ts +13 -1
- package/lib/utils/SimpleConnector.d.ts +3 -2
- package/lib/utils/assert.d.ts +0 -1
- package/lib/utils/relationPath.js +1 -1
- package/package.json +1 -1
- package/src/entities/Log.ts +2 -18
- package/src/entities/ModiEntity.ts +26 -26
- package/src/entities/OperEntity.ts +27 -27
- package/src/entities/Relation.ts +43 -43
- package/src/entities/UserEntityClaim.ts +29 -29
- package/src/entities/UserEntityGrant.ts +24 -24
- package/src/entities/UserRelation.ts +50 -50
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { ActionDef } from "../../types/Action";
|
|
2
1
|
import { GenericAction } from "../../actions/action";
|
|
3
|
-
export type IState = 'normal' | 'rollbacked' | string;
|
|
4
|
-
export type State = IState | string;
|
|
5
2
|
export type IAction = 'undo' | 'redo' | string;
|
|
6
3
|
export type ParticularAction = IAction;
|
|
7
4
|
export declare const actions: string[];
|
|
8
|
-
export declare const IActionDef: ActionDef<IAction, IState>;
|
|
9
5
|
export type Action = GenericAction | ParticularAction | string;
|
|
10
|
-
export declare const actionDefDict: {
|
|
11
|
-
iState: ActionDef<string, string>;
|
|
12
|
-
};
|
|
6
|
+
export declare const actionDefDict: {};
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.actionDefDict = exports.
|
|
3
|
+
exports.actionDefDict = exports.actions = void 0;
|
|
4
4
|
exports.actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "undo", "redo"];
|
|
5
|
-
exports.
|
|
6
|
-
stm: {
|
|
7
|
-
undo: ['normal', 'rollbacked'],
|
|
8
|
-
redo: ['rollbacked', 'normal'],
|
|
9
|
-
},
|
|
10
|
-
is: 'normal',
|
|
11
|
-
};
|
|
12
|
-
exports.actionDefDict = {
|
|
13
|
-
iState: exports.IActionDef
|
|
14
|
-
};
|
|
5
|
+
exports.actionDefDict = {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey, SubQueryPredicateMetadata } from "../../types/Demand";
|
|
2
2
|
import { OneOf } from "../../types/Polyfill";
|
|
3
3
|
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "../../types/Entity";
|
|
4
|
-
import { Action, ParticularAction
|
|
4
|
+
import { Action, ParticularAction } from "./Action";
|
|
5
5
|
import { String } from "../../types/DataType";
|
|
6
6
|
import * as Oper from "../Oper/Schema";
|
|
7
7
|
import * as ModiEntity from "../ModiEntity/Schema";
|
|
@@ -9,13 +9,13 @@ import * as OperEntity from "../OperEntity/Schema";
|
|
|
9
9
|
export type OpSchema = EntityShape & {
|
|
10
10
|
entity: String<32>;
|
|
11
11
|
entityId: String<64>;
|
|
12
|
-
iState
|
|
12
|
+
iState: "normal" | "rollbacked";
|
|
13
13
|
};
|
|
14
14
|
export type OpAttr = keyof OpSchema;
|
|
15
15
|
export type Schema = EntityShape & {
|
|
16
16
|
entity: String<32>;
|
|
17
17
|
entityId: String<64>;
|
|
18
|
-
iState
|
|
18
|
+
iState: "normal" | "rollbacked";
|
|
19
19
|
oper$log?: Array<Oper.Schema>;
|
|
20
20
|
oper$log$$aggr?: AggregationResult<Oper.Schema>;
|
|
21
21
|
modiEntity$entity?: Array<ModiEntity.Schema>;
|
|
@@ -32,7 +32,7 @@ type AttrFilter = {
|
|
|
32
32
|
$$updateAt$$: Q_DateValue;
|
|
33
33
|
entity: Q_StringValue;
|
|
34
34
|
entityId: Q_StringValue;
|
|
35
|
-
iState: Q_EnumValue<
|
|
35
|
+
iState: Q_EnumValue<"normal" | "rollbacked">;
|
|
36
36
|
oper$log: Oper.Filter & SubQueryPredicateMetadata;
|
|
37
37
|
modiEntity$entity: ModiEntity.Filter & SubQueryPredicateMetadata;
|
|
38
38
|
operEntity$entity: OperEntity.Filter & SubQueryPredicateMetadata;
|
package/lib/entities/Log.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import { ActionDef } from '../types/Action';
|
|
2
1
|
import { String } from '../types/DataType';
|
|
3
2
|
import { EntityShape } from '../types/Entity';
|
|
4
3
|
import { EntityDesc } from '../types/EntityDesc';
|
|
5
4
|
export interface Schema extends EntityShape {
|
|
6
5
|
entity: String<32>;
|
|
7
6
|
entityId: String<64>;
|
|
7
|
+
iState: 'normal' | 'rollbacked';
|
|
8
8
|
}
|
|
9
|
-
export type IState = 'normal' | 'rollbacked';
|
|
10
|
-
export type State = IState;
|
|
11
9
|
export type IAction = 'undo' | 'redo';
|
|
12
10
|
export type Action = IAction;
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const entityDesc: EntityDesc<Schema, Action, '', {
|
|
15
|
-
iState: IState;
|
|
16
|
-
}>;
|
|
11
|
+
export declare const entityDesc: EntityDesc<Schema, Action>;
|
package/lib/entities/Log.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.entityDesc =
|
|
3
|
+
exports.entityDesc = void 0;
|
|
4
4
|
;
|
|
5
|
-
exports.IActionDef = {
|
|
6
|
-
stm: {
|
|
7
|
-
undo: ['normal', 'rollbacked'],
|
|
8
|
-
redo: ['rollbacked', 'normal'],
|
|
9
|
-
},
|
|
10
|
-
is: 'normal',
|
|
11
|
-
};
|
|
12
5
|
exports.entityDesc = {
|
|
13
6
|
locales: {
|
|
14
7
|
zh_CN: {
|
|
@@ -35,11 +28,5 @@ exports.entityDesc = {
|
|
|
35
28
|
undo: '',
|
|
36
29
|
redo: '',
|
|
37
30
|
},
|
|
38
|
-
color: {
|
|
39
|
-
iState: {
|
|
40
|
-
normal: '#229954',
|
|
41
|
-
rollbacked: '#2C3E50',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
31
|
},
|
|
45
32
|
};
|
|
@@ -22,8 +22,8 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
22
22
|
selectionRewriters = [];
|
|
23
23
|
operationRewriters = [];
|
|
24
24
|
async reinforceSelectionAsync(entity, selection, context, option, isAggr) {
|
|
25
|
-
if (!isAggr && !selection.distinct
|
|
26
|
-
this.reinforceSelectionInner(entity, selection, context);
|
|
25
|
+
if (!isAggr && !selection.distinct) {
|
|
26
|
+
this.reinforceSelectionInner(entity, selection, context, option);
|
|
27
27
|
}
|
|
28
28
|
const rewriterPromises = this.selectionRewriters.map(ele => ele(this.getSchema(), entity, selection, context, option, isAggr));
|
|
29
29
|
if (rewriterPromises.length > 0) {
|
|
@@ -32,14 +32,14 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
32
32
|
}
|
|
33
33
|
reinforceSelectionSync(entity, selection, context, option, isAggr) {
|
|
34
34
|
if (!isAggr && !selection.distinct) {
|
|
35
|
-
this.reinforceSelectionInner(entity, selection, context);
|
|
35
|
+
this.reinforceSelectionInner(entity, selection, context, option);
|
|
36
36
|
}
|
|
37
37
|
this.selectionRewriters.forEach(ele => {
|
|
38
38
|
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
|
39
39
|
(0, assert_1.default)(!(result instanceof Promise));
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
reinforceSelectionInner(entity, selection, context) {
|
|
42
|
+
reinforceSelectionInner(entity, selection, context, option) {
|
|
43
43
|
const { filter, data, sorter } = selection;
|
|
44
44
|
const assignNecessaryProjectionAttrs = (projectionNode, attrs) => {
|
|
45
45
|
attrs.forEach((attr) => {
|
|
@@ -240,7 +240,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
240
240
|
else {
|
|
241
241
|
assignNecessaryProjectionAttrs(data, ['entity', 'entityId']);
|
|
242
242
|
}
|
|
243
|
-
this.reinforceSelectionInner(rel[0], projectionNode[attr], context);
|
|
243
|
+
this.reinforceSelectionInner(rel[0], projectionNode[attr], context, option);
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
}
|
|
@@ -272,7 +272,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
272
272
|
// 如果对象上有relation关系,在此将本用户相关的relation和actionAuth全部取出
|
|
273
273
|
// 还要将actionAuth上没有relation关系但destEntity为本对象的行也全部取出,这些是指向userId的可能路径
|
|
274
274
|
// 放在这里有点怪异,暂先这样
|
|
275
|
-
if (context instanceof AsyncRowStore_1.AsyncContext) {
|
|
275
|
+
if (context instanceof AsyncRowStore_1.AsyncContext && !option.dontCollect) {
|
|
276
276
|
const userId = context.getCurrentUserId(true);
|
|
277
277
|
if (userId && !entities_1.SYSTEM_RESERVE_ENTITIES.includes(entity2)) {
|
|
278
278
|
if (this.getSchema()[entity2].relation && !projectionNode.userRelation$entity) {
|
|
@@ -335,10 +335,12 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
335
335
|
const cascadeSelectionFns = [];
|
|
336
336
|
const supportMtoJoin = this.supportManyToOneJoin();
|
|
337
337
|
const { toModi } = this.getSchema()[entity];
|
|
338
|
+
const projection = {};
|
|
338
339
|
(0, assert_1.default)(typeof projection2 === 'object');
|
|
339
340
|
for (const attr in projection2) {
|
|
340
341
|
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
341
342
|
if (relation === 1 || relation == 0) {
|
|
343
|
+
projection[attr] = projection2[attr];
|
|
342
344
|
}
|
|
343
345
|
else if (relation === 2) {
|
|
344
346
|
// 基于entity/entityId的多对一
|
|
@@ -368,6 +370,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
368
370
|
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
369
371
|
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
370
372
|
}));
|
|
373
|
+
projection[attr] = subProjection;
|
|
371
374
|
}
|
|
372
375
|
else {
|
|
373
376
|
cascadeSelectionFns.push((result) => {
|
|
@@ -455,6 +458,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
455
458
|
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
456
459
|
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
457
460
|
}));
|
|
461
|
+
projection[attr] = subProjection;
|
|
458
462
|
}
|
|
459
463
|
else {
|
|
460
464
|
cascadeSelectionFns.push((result) => {
|
|
@@ -644,7 +648,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
644
648
|
}
|
|
645
649
|
}
|
|
646
650
|
return {
|
|
647
|
-
projection
|
|
651
|
+
projection,
|
|
648
652
|
cascadeSelectionFns,
|
|
649
653
|
};
|
|
650
654
|
}
|
|
@@ -702,7 +706,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
702
706
|
option2.modiParentEntity = entity;
|
|
703
707
|
}
|
|
704
708
|
}
|
|
705
|
-
if (toLog && action !== 'create') {
|
|
709
|
+
if (toLog && action !== 'create' && !option.dontCreateOper) {
|
|
706
710
|
// 此对象(及其下辖的cascade更新都需要记录log)
|
|
707
711
|
(0, assert_1.default)(typeof filter.id === 'string');
|
|
708
712
|
(0, assert_1.default)(!option2.logId, 'undo无法支持嵌套');
|
|
@@ -714,6 +718,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
714
718
|
id: (0, uuid_1.generateNewId)(),
|
|
715
719
|
data: {
|
|
716
720
|
id: logId,
|
|
721
|
+
iState: 'normal',
|
|
717
722
|
},
|
|
718
723
|
action: 'create',
|
|
719
724
|
}
|
|
@@ -1225,7 +1230,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1225
1230
|
};
|
|
1226
1231
|
const closeRootMode = context.openRootMode();
|
|
1227
1232
|
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1228
|
-
dontCollect:
|
|
1233
|
+
dontCollect: !option.logId, // 如果是在创建log,则把oper收集回去
|
|
1229
1234
|
});
|
|
1230
1235
|
closeRootMode();
|
|
1231
1236
|
}
|
|
@@ -1269,6 +1274,10 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1269
1274
|
});
|
|
1270
1275
|
});
|
|
1271
1276
|
}
|
|
1277
|
+
if (ids.length === 0) {
|
|
1278
|
+
// 如果没有行需要更新/删除,直接返回
|
|
1279
|
+
return {};
|
|
1280
|
+
}
|
|
1272
1281
|
if (data) {
|
|
1273
1282
|
this.preProcessDataUpdated(action, data, true);
|
|
1274
1283
|
}
|
|
@@ -1398,7 +1407,7 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1398
1407
|
};
|
|
1399
1408
|
const closeRootMode = context.openRootMode();
|
|
1400
1409
|
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1401
|
-
dontCollect:
|
|
1410
|
+
dontCollect: !option.logId, // 如果是在创建log,则把oper收集回去
|
|
1402
1411
|
});
|
|
1403
1412
|
closeRootMode();
|
|
1404
1413
|
}
|
|
@@ -79,7 +79,7 @@ function createUniqueCheckers(schema) {
|
|
|
79
79
|
entity,
|
|
80
80
|
action: 'create',
|
|
81
81
|
type: 'logicalData',
|
|
82
|
-
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
82
|
+
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
83
83
|
checker: (operation, context) => {
|
|
84
84
|
const { data } = operation;
|
|
85
85
|
if (data instanceof Array) {
|
|
@@ -95,9 +95,9 @@ function createUniqueCheckers(schema) {
|
|
|
95
95
|
}
|
|
96
96
|
}, {
|
|
97
97
|
entity,
|
|
98
|
-
action: 'update',
|
|
98
|
+
action: 'update',
|
|
99
99
|
type: 'logicalData',
|
|
100
|
-
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
100
|
+
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
101
101
|
checker: (operation, context) => {
|
|
102
102
|
const { data, filter: operationFilter } = operation;
|
|
103
103
|
if (data) {
|
|
@@ -227,7 +227,7 @@ function createActionTransformerCheckers(actionDefDict) {
|
|
|
227
227
|
action: 'create',
|
|
228
228
|
type: 'logical',
|
|
229
229
|
entity,
|
|
230
|
-
priority: 10,
|
|
230
|
+
priority: 10,
|
|
231
231
|
checker: (operation) => {
|
|
232
232
|
const { data } = operation;
|
|
233
233
|
if (data instanceof Array) {
|
package/lib/store/triggers.js
CHANGED
|
@@ -46,7 +46,6 @@ async function undoLog(log, context) {
|
|
|
46
46
|
filter: filter,
|
|
47
47
|
}, {
|
|
48
48
|
blockTrigger: true,
|
|
49
|
-
dontCollect: true,
|
|
50
49
|
dontCreateOper: true,
|
|
51
50
|
});
|
|
52
51
|
break;
|
|
@@ -61,7 +60,6 @@ async function undoLog(log, context) {
|
|
|
61
60
|
data: undoData[id],
|
|
62
61
|
}, {
|
|
63
62
|
blockTrigger: true,
|
|
64
|
-
dontCollect: true,
|
|
65
63
|
dontCreateOper: true,
|
|
66
64
|
});
|
|
67
65
|
}
|
|
@@ -81,7 +79,6 @@ async function undoLog(log, context) {
|
|
|
81
79
|
}
|
|
82
80
|
}, {
|
|
83
81
|
blockTrigger: true,
|
|
84
|
-
dontCollect: true,
|
|
85
82
|
dontCreateOper: true,
|
|
86
83
|
});
|
|
87
84
|
}
|
|
@@ -89,20 +86,24 @@ async function undoLog(log, context) {
|
|
|
89
86
|
}
|
|
90
87
|
}
|
|
91
88
|
}
|
|
92
|
-
await context.operate('
|
|
89
|
+
await context.operate('log', {
|
|
93
90
|
id: 'dummy',
|
|
94
91
|
action: 'undo',
|
|
95
92
|
data: {
|
|
96
93
|
iState: 'rollbacked',
|
|
94
|
+
oper$log: {
|
|
95
|
+
id: 'dummy',
|
|
96
|
+
action: 'undo',
|
|
97
|
+
data: {
|
|
98
|
+
iState: 'rollbacked',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
97
101
|
},
|
|
98
102
|
filter: {
|
|
99
|
-
id:
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
+
id: log.id,
|
|
104
|
+
},
|
|
103
105
|
}, {
|
|
104
106
|
blockTrigger: true,
|
|
105
|
-
dontCollect: true,
|
|
106
107
|
dontCreateOper: true,
|
|
107
108
|
});
|
|
108
109
|
}
|
|
@@ -124,7 +125,6 @@ async function redoLog(log, context) {
|
|
|
124
125
|
data: data,
|
|
125
126
|
}, {
|
|
126
127
|
blockTrigger: true,
|
|
127
|
-
dontCollect: true,
|
|
128
128
|
dontCreateOper: true,
|
|
129
129
|
});
|
|
130
130
|
break;
|
|
@@ -137,7 +137,6 @@ async function redoLog(log, context) {
|
|
|
137
137
|
filter: filter,
|
|
138
138
|
}, {
|
|
139
139
|
blockTrigger: true,
|
|
140
|
-
dontCollect: true,
|
|
141
140
|
dontCreateOper: true,
|
|
142
141
|
});
|
|
143
142
|
break;
|
|
@@ -150,27 +149,30 @@ async function redoLog(log, context) {
|
|
|
150
149
|
filter: filter,
|
|
151
150
|
}, {
|
|
152
151
|
blockTrigger: true,
|
|
153
|
-
dontCollect: true,
|
|
154
152
|
dontCreateOper: true,
|
|
155
153
|
});
|
|
156
154
|
break;
|
|
157
155
|
}
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
|
-
await context.operate('
|
|
158
|
+
await context.operate('log', {
|
|
161
159
|
id: 'dummy',
|
|
162
160
|
action: 'redo',
|
|
163
161
|
data: {
|
|
164
162
|
iState: 'normal',
|
|
163
|
+
oper$log: {
|
|
164
|
+
id: 'dummy',
|
|
165
|
+
action: 'redo',
|
|
166
|
+
data: {
|
|
167
|
+
iState: 'normal',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
165
170
|
},
|
|
166
171
|
filter: {
|
|
167
|
-
id:
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
}
|
|
172
|
+
id: log.id,
|
|
173
|
+
},
|
|
171
174
|
}, {
|
|
172
175
|
blockTrigger: true,
|
|
173
|
-
dontCollect: true,
|
|
174
176
|
dontCreateOper: true,
|
|
175
177
|
});
|
|
176
178
|
}
|
|
@@ -201,11 +203,12 @@ exports.logTriggers = [
|
|
|
201
203
|
},
|
|
202
204
|
entity: 1,
|
|
203
205
|
entityId: 1,
|
|
206
|
+
[types_1.SeqAttribute]: 1,
|
|
204
207
|
},
|
|
205
208
|
filter: {
|
|
206
209
|
id: ids[0],
|
|
207
210
|
},
|
|
208
|
-
}, {});
|
|
211
|
+
}, { dontCollect: true });
|
|
209
212
|
const logs = await context.select('log', {
|
|
210
213
|
data: {
|
|
211
214
|
id: 1,
|
|
@@ -217,8 +220,13 @@ exports.logTriggers = [
|
|
|
217
220
|
action: 1,
|
|
218
221
|
targetEntity: 1,
|
|
219
222
|
filter: 1,
|
|
223
|
+
iState: 1,
|
|
220
224
|
},
|
|
221
225
|
},
|
|
226
|
+
iState: 1,
|
|
227
|
+
entity: 1,
|
|
228
|
+
entityId: 1,
|
|
229
|
+
[types_1.SeqAttribute]: 1,
|
|
222
230
|
},
|
|
223
231
|
filter: {
|
|
224
232
|
iState: 'normal',
|
|
@@ -271,11 +279,12 @@ exports.logTriggers = [
|
|
|
271
279
|
iState: 1,
|
|
272
280
|
entity: 1,
|
|
273
281
|
entityId: 1,
|
|
282
|
+
[types_1.SeqAttribute]: 1,
|
|
274
283
|
},
|
|
275
284
|
filter: {
|
|
276
285
|
id: ids[0],
|
|
277
286
|
},
|
|
278
|
-
}, {});
|
|
287
|
+
}, { dontCollect: true });
|
|
279
288
|
const logs = await context.select('log', {
|
|
280
289
|
data: {
|
|
281
290
|
id: 1,
|
|
@@ -287,8 +296,13 @@ exports.logTriggers = [
|
|
|
287
296
|
action: 1,
|
|
288
297
|
targetEntity: 1,
|
|
289
298
|
filter: 1,
|
|
299
|
+
iState: 1,
|
|
290
300
|
},
|
|
291
301
|
},
|
|
302
|
+
iState: 1,
|
|
303
|
+
entity: 1,
|
|
304
|
+
entityId: 1,
|
|
305
|
+
[types_1.SeqAttribute]: 1,
|
|
292
306
|
},
|
|
293
307
|
filter: {
|
|
294
308
|
iState: 'rollbacked',
|
|
@@ -345,7 +359,7 @@ exports.logTriggers = [
|
|
|
345
359
|
iState: 'rollbacked',
|
|
346
360
|
},
|
|
347
361
|
}, {});
|
|
348
|
-
return (result.oper
|
|
362
|
+
return (result.oper?.remove || 0) + (result2.log?.remove || 0);
|
|
349
363
|
}
|
|
350
364
|
}
|
|
351
365
|
];
|
package/lib/types/Connector.d.ts
CHANGED
|
@@ -21,7 +21,9 @@ export interface Connector<ED extends EntityDict & BaseEntityDict, FrontCxt exte
|
|
|
21
21
|
headers?: Record<string, any>;
|
|
22
22
|
}>;
|
|
23
23
|
serializeException: (exception: OakException<ED>, headers: IncomingHttpHeaders, body: any) => {
|
|
24
|
-
body:
|
|
24
|
+
body: {
|
|
25
|
+
exception: string;
|
|
26
|
+
};
|
|
25
27
|
headers?: Record<string, any>;
|
|
26
28
|
};
|
|
27
29
|
getSubscribeRouter: () => string;
|
package/lib/types/Sync.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface SyncRemoteConfigBase<ED extends EntityDict & BaseEntityDict, Cx
|
|
|
51
51
|
pushEntities?: Array<PushEntityDef<ED, keyof ED, Cxt>>;
|
|
52
52
|
pullEntities?: Array<PullEntityDef<ED, keyof ED, Cxt>>;
|
|
53
53
|
}
|
|
54
|
-
interface SyncRemoteConfig<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends SyncRemoteConfigBase<ED, Cxt> {
|
|
54
|
+
export interface SyncRemoteConfig<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends SyncRemoteConfigBase<ED, Cxt> {
|
|
55
55
|
getPushInfo: (context: Cxt, option: {
|
|
56
56
|
remoteEntityId: string;
|
|
57
57
|
userId: string;
|
|
@@ -60,6 +60,18 @@ interface SyncRemoteConfig<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
|
|
60
60
|
selfId: string;
|
|
61
61
|
remoteEntityId: string;
|
|
62
62
|
}) => Promise<RemotePullInfo>;
|
|
63
|
+
/**
|
|
64
|
+
* 同步失败回调
|
|
65
|
+
*/
|
|
66
|
+
onFailed?: (result: {
|
|
67
|
+
data: {
|
|
68
|
+
entity: keyof ED;
|
|
69
|
+
action: ED[keyof ED]['Action'];
|
|
70
|
+
data: ED[keyof ED]['Operation']['data'];
|
|
71
|
+
rowIds: string[];
|
|
72
|
+
}[];
|
|
73
|
+
reason: Error;
|
|
74
|
+
}, context: Cxt) => Promise<void>;
|
|
63
75
|
}
|
|
64
76
|
export interface SyncSelfConfigBase<ED extends EntityDict & BaseEntityDict> {
|
|
65
77
|
endpoint?: string;
|
|
@@ -60,8 +60,9 @@ export default class SimpleConnector<ED extends EntityDict & BaseEntityDict, Fro
|
|
|
60
60
|
headers?: Record<string, any> | undefined;
|
|
61
61
|
}>;
|
|
62
62
|
serializeException(exception: OakException<ED>, headers: IncomingHttpHeaders, body: any): {
|
|
63
|
-
body:
|
|
64
|
-
|
|
63
|
+
body: {
|
|
64
|
+
exception: string;
|
|
65
|
+
};
|
|
65
66
|
};
|
|
66
67
|
getBridgeRouter(): string;
|
|
67
68
|
/**
|
package/lib/utils/assert.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ function destructRelationPath(schema, entity, path, relationFilter, recursive) {
|
|
|
60
60
|
},
|
|
61
61
|
filter: relationFilter,
|
|
62
62
|
} // as ED['userRelation']['Selection']
|
|
63
|
-
},
|
|
63
|
+
},
|
|
64
64
|
getData: (d) => {
|
|
65
65
|
return d.userRelation$entity;
|
|
66
66
|
},
|
package/package.json
CHANGED
package/src/entities/Log.ts
CHANGED
|
@@ -6,24 +6,14 @@ import { EntityDesc } from '../types/EntityDesc';
|
|
|
6
6
|
export interface Schema extends EntityShape {
|
|
7
7
|
entity: String<32>; // 关联的目标对象
|
|
8
8
|
entityId: String<64>; // 关联的目标对象id
|
|
9
|
+
iState: 'normal' | 'rollbacked';
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
export type IState = 'normal' | 'rollbacked';
|
|
12
|
-
export type State = IState;
|
|
13
12
|
export type IAction = 'undo' | 'redo';
|
|
14
13
|
export type Action = IAction;
|
|
15
|
-
export const IActionDef: ActionDef<IAction, IState> = {
|
|
16
|
-
stm: {
|
|
17
|
-
undo: ['normal', 'rollbacked'],
|
|
18
|
-
redo: ['rollbacked', 'normal'],
|
|
19
|
-
},
|
|
20
|
-
is: 'normal',
|
|
21
|
-
};
|
|
22
14
|
|
|
23
15
|
|
|
24
|
-
export const entityDesc: EntityDesc<Schema, Action
|
|
25
|
-
iState: IState,
|
|
26
|
-
}> = {
|
|
16
|
+
export const entityDesc: EntityDesc<Schema, Action> = {
|
|
27
17
|
locales: {
|
|
28
18
|
zh_CN: {
|
|
29
19
|
name: '日志',
|
|
@@ -49,11 +39,5 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|
|
49
39
|
undo: '',
|
|
50
40
|
redo: '',
|
|
51
41
|
},
|
|
52
|
-
color: {
|
|
53
|
-
iState: {
|
|
54
|
-
normal: '#229954',
|
|
55
|
-
rollbacked: '#2C3E50',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
42
|
},
|
|
59
43
|
};
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { EntityShape } from '../types/Entity';
|
|
3
|
-
import { Schema as Modi } from './Modi';
|
|
4
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
5
|
-
|
|
6
|
-
export interface Schema extends EntityShape {
|
|
7
|
-
modi: Modi,
|
|
8
|
-
entity: String<32>;
|
|
9
|
-
entityId: String<64>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const entityDesc: EntityDesc<Schema> = {
|
|
13
|
-
locales: {
|
|
14
|
-
zh_CN: {
|
|
15
|
-
name: '更新对象连接',
|
|
16
|
-
attr: {
|
|
17
|
-
modi: '更新',
|
|
18
|
-
entity: '关联对象',
|
|
19
|
-
entityId: '关联对象id',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
configuration: {
|
|
24
|
-
actionType: 'appendOnly',
|
|
25
|
-
}
|
|
26
|
-
};
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { EntityShape } from '../types/Entity';
|
|
3
|
+
import { Schema as Modi } from './Modi';
|
|
4
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
5
|
+
|
|
6
|
+
export interface Schema extends EntityShape {
|
|
7
|
+
modi: Modi,
|
|
8
|
+
entity: String<32>;
|
|
9
|
+
entityId: String<64>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const entityDesc: EntityDesc<Schema> = {
|
|
13
|
+
locales: {
|
|
14
|
+
zh_CN: {
|
|
15
|
+
name: '更新对象连接',
|
|
16
|
+
attr: {
|
|
17
|
+
modi: '更新',
|
|
18
|
+
entity: '关联对象',
|
|
19
|
+
entityId: '关联对象id',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
configuration: {
|
|
24
|
+
actionType: 'appendOnly',
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { EntityShape, Configuration } from '../types/Entity';
|
|
3
|
-
import { LocaleDef } from '../types/Locale';
|
|
4
|
-
import { Schema as Oper } from './Oper';
|
|
5
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
6
|
-
|
|
7
|
-
export interface Schema extends EntityShape {
|
|
8
|
-
oper: Oper,
|
|
9
|
-
entity: String<32>;
|
|
10
|
-
entityId: String<64>;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const entityDesc: EntityDesc<Schema> = {
|
|
14
|
-
locales: {
|
|
15
|
-
zh_CN: {
|
|
16
|
-
name: '操作对象连接',
|
|
17
|
-
attr: {
|
|
18
|
-
oper: '操作',
|
|
19
|
-
entity: '关联对象',
|
|
20
|
-
entityId: '关联对象id',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
configuration: {
|
|
25
|
-
actionType: 'appendOnly',
|
|
26
|
-
}
|
|
27
|
-
};
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { EntityShape, Configuration } from '../types/Entity';
|
|
3
|
+
import { LocaleDef } from '../types/Locale';
|
|
4
|
+
import { Schema as Oper } from './Oper';
|
|
5
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
6
|
+
|
|
7
|
+
export interface Schema extends EntityShape {
|
|
8
|
+
oper: Oper,
|
|
9
|
+
entity: String<32>;
|
|
10
|
+
entityId: String<64>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const entityDesc: EntityDesc<Schema> = {
|
|
14
|
+
locales: {
|
|
15
|
+
zh_CN: {
|
|
16
|
+
name: '操作对象连接',
|
|
17
|
+
attr: {
|
|
18
|
+
oper: '操作',
|
|
19
|
+
entity: '关联对象',
|
|
20
|
+
entityId: '关联对象id',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
configuration: {
|
|
25
|
+
actionType: 'appendOnly',
|
|
26
|
+
}
|
|
27
|
+
};
|
package/src/entities/Relation.ts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { EntityShape } from '../types/Entity';
|
|
3
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
-
|
|
5
|
-
export interface Schema extends EntityShape {
|
|
6
|
-
entity: String<32>;
|
|
7
|
-
entityId?: String<64>; // 可以为空
|
|
8
|
-
name?: String<32>;
|
|
9
|
-
display?: String<32>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const entityDesc: EntityDesc<Schema> = {
|
|
13
|
-
locales: {
|
|
14
|
-
zh_CN: {
|
|
15
|
-
name: '用户授权',
|
|
16
|
-
attr: {
|
|
17
|
-
name: '关系',
|
|
18
|
-
entity: '目标对象',
|
|
19
|
-
entityId: '目标对象id',
|
|
20
|
-
display: '显示值',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
indexes: [
|
|
25
|
-
{
|
|
26
|
-
name: 'index_targetEntity_entityId_name',
|
|
27
|
-
attributes: [
|
|
28
|
-
{
|
|
29
|
-
name: 'entity',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'entityId',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'name',
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
config: {
|
|
39
|
-
unique: true,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
]
|
|
43
|
-
};
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { EntityShape } from '../types/Entity';
|
|
3
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
+
|
|
5
|
+
export interface Schema extends EntityShape {
|
|
6
|
+
entity: String<32>;
|
|
7
|
+
entityId?: String<64>; // 可以为空
|
|
8
|
+
name?: String<32>;
|
|
9
|
+
display?: String<32>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const entityDesc: EntityDesc<Schema> = {
|
|
13
|
+
locales: {
|
|
14
|
+
zh_CN: {
|
|
15
|
+
name: '用户授权',
|
|
16
|
+
attr: {
|
|
17
|
+
name: '关系',
|
|
18
|
+
entity: '目标对象',
|
|
19
|
+
entityId: '目标对象id',
|
|
20
|
+
display: '显示值',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
indexes: [
|
|
25
|
+
{
|
|
26
|
+
name: 'index_targetEntity_entityId_name',
|
|
27
|
+
attributes: [
|
|
28
|
+
{
|
|
29
|
+
name: 'entity',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'entityId',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'name',
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
config: {
|
|
39
|
+
unique: true,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
};
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { EntityShape } from '../types/Entity';
|
|
3
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
-
import { Schema as UserEntityGrant } from './UserEntityGrant';
|
|
5
|
-
import { Schema as User } from './User';
|
|
6
|
-
import { Schema as Relation } from './Relation';
|
|
7
|
-
import { Schema as UserRelation } from './UserRelation';
|
|
8
|
-
|
|
9
|
-
export interface Schema extends EntityShape {
|
|
10
|
-
ueg: UserEntityGrant;
|
|
11
|
-
user: User;
|
|
12
|
-
relation: Relation;
|
|
13
|
-
claimEntityId: String<64>;
|
|
14
|
-
userRelation: UserRelation;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const entityDesc: EntityDesc<Schema, ''> = {
|
|
18
|
-
locales: {
|
|
19
|
-
zh_CN: {
|
|
20
|
-
name: '用户授权领取',
|
|
21
|
-
attr: {
|
|
22
|
-
ueg: '授权',
|
|
23
|
-
user: '用户',
|
|
24
|
-
relation: '关系',
|
|
25
|
-
claimEntityId: '对象Id',
|
|
26
|
-
userRelation: '用户关系',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { EntityShape } from '../types/Entity';
|
|
3
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
+
import { Schema as UserEntityGrant } from './UserEntityGrant';
|
|
5
|
+
import { Schema as User } from './User';
|
|
6
|
+
import { Schema as Relation } from './Relation';
|
|
7
|
+
import { Schema as UserRelation } from './UserRelation';
|
|
8
|
+
|
|
9
|
+
export interface Schema extends EntityShape {
|
|
10
|
+
ueg: UserEntityGrant;
|
|
11
|
+
user: User;
|
|
12
|
+
relation: Relation;
|
|
13
|
+
claimEntityId: String<64>;
|
|
14
|
+
userRelation: UserRelation;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const entityDesc: EntityDesc<Schema, ''> = {
|
|
18
|
+
locales: {
|
|
19
|
+
zh_CN: {
|
|
20
|
+
name: '用户授权领取',
|
|
21
|
+
attr: {
|
|
22
|
+
ueg: '授权',
|
|
23
|
+
user: '用户',
|
|
24
|
+
relation: '关系',
|
|
25
|
+
claimEntityId: '对象Id',
|
|
26
|
+
userRelation: '用户关系',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
30
|
};
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { EntityShape } from '../types/Entity';
|
|
3
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
-
|
|
5
|
-
type RelationIds = string[];
|
|
6
|
-
|
|
7
|
-
export interface Schema extends EntityShape {
|
|
8
|
-
relationEntity: String<32>;
|
|
9
|
-
relationEntityFilter: Object;
|
|
10
|
-
relationIds: RelationIds;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const entityDesc: EntityDesc<Schema, ''> = {
|
|
14
|
-
locales: {
|
|
15
|
-
zh_CN: {
|
|
16
|
-
name: '用户授权',
|
|
17
|
-
attr: {
|
|
18
|
-
relationIds: '关系',
|
|
19
|
-
relationEntity: '关联对象',
|
|
20
|
-
relationEntityFilter: '对象限定条件',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
};
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { EntityShape } from '../types/Entity';
|
|
3
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
4
|
+
|
|
5
|
+
type RelationIds = string[];
|
|
6
|
+
|
|
7
|
+
export interface Schema extends EntityShape {
|
|
8
|
+
relationEntity: String<32>;
|
|
9
|
+
relationEntityFilter: Object;
|
|
10
|
+
relationIds: RelationIds;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const entityDesc: EntityDesc<Schema, ''> = {
|
|
14
|
+
locales: {
|
|
15
|
+
zh_CN: {
|
|
16
|
+
name: '用户授权',
|
|
17
|
+
attr: {
|
|
18
|
+
relationIds: '关系',
|
|
19
|
+
relationEntity: '关联对象',
|
|
20
|
+
relationEntityFilter: '对象限定条件',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { String } from '../types/DataType';
|
|
2
|
-
import { LocaleDef } from '../types/Locale';
|
|
3
|
-
import { EntityShape } from '../types/Entity';
|
|
4
|
-
import { Index } from '../types/Storage';
|
|
5
|
-
import { Schema as User } from './User';
|
|
6
|
-
import { Schema as Relation } from './Relation';
|
|
7
|
-
import { EntityDesc } from '../types/EntityDesc';
|
|
8
|
-
|
|
9
|
-
export interface Schema extends EntityShape {
|
|
10
|
-
user: User;
|
|
11
|
-
relation: Relation;
|
|
12
|
-
entity: String<32>;
|
|
13
|
-
entityId: String<64>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const entityDesc: EntityDesc<Schema> = {
|
|
17
|
-
locales: {
|
|
18
|
-
zh_CN: {
|
|
19
|
-
name: '用户对象关系',
|
|
20
|
-
attr: {
|
|
21
|
-
user: '关系',
|
|
22
|
-
relation: '目标关系',
|
|
23
|
-
entity: '目标对象',
|
|
24
|
-
entityId: '目标对象ID',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
indexes: [
|
|
29
|
-
{
|
|
30
|
-
name: 'index_user_entity_entityId_relation',
|
|
31
|
-
attributes: [
|
|
32
|
-
{
|
|
33
|
-
name: 'user',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'entity',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: 'entityId',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'relation',
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
config: {
|
|
46
|
-
unique: true,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
]
|
|
50
|
-
};
|
|
1
|
+
import { String } from '../types/DataType';
|
|
2
|
+
import { LocaleDef } from '../types/Locale';
|
|
3
|
+
import { EntityShape } from '../types/Entity';
|
|
4
|
+
import { Index } from '../types/Storage';
|
|
5
|
+
import { Schema as User } from './User';
|
|
6
|
+
import { Schema as Relation } from './Relation';
|
|
7
|
+
import { EntityDesc } from '../types/EntityDesc';
|
|
8
|
+
|
|
9
|
+
export interface Schema extends EntityShape {
|
|
10
|
+
user: User;
|
|
11
|
+
relation: Relation;
|
|
12
|
+
entity: String<32>;
|
|
13
|
+
entityId: String<64>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const entityDesc: EntityDesc<Schema> = {
|
|
17
|
+
locales: {
|
|
18
|
+
zh_CN: {
|
|
19
|
+
name: '用户对象关系',
|
|
20
|
+
attr: {
|
|
21
|
+
user: '关系',
|
|
22
|
+
relation: '目标关系',
|
|
23
|
+
entity: '目标对象',
|
|
24
|
+
entityId: '目标对象ID',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
indexes: [
|
|
29
|
+
{
|
|
30
|
+
name: 'index_user_entity_entityId_relation',
|
|
31
|
+
attributes: [
|
|
32
|
+
{
|
|
33
|
+
name: 'user',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'entity',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'entityId',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'relation',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
config: {
|
|
46
|
+
unique: true,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
};
|