oak-domain 5.1.19 → 5.1.21
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/ActionAuth/_baseSchema.d.ts +2 -0
- package/lib/base-app-domain/ModiEntity/_baseSchema.d.ts +1 -0
- package/lib/base-app-domain/Oper/_baseSchema.d.ts +2 -0
- package/lib/base-app-domain/OperEntity/_baseSchema.d.ts +1 -0
- package/lib/base-app-domain/RelationAuth/_baseSchema.d.ts +3 -0
- package/lib/base-app-domain/User/_baseSchema.d.ts +1 -0
- package/lib/base-app-domain/UserEntityClaim/_baseSchema.d.ts +4 -0
- package/lib/base-app-domain/UserRelation/_baseSchema.d.ts +2 -0
- package/lib/compiler/dependencyBuilder.js +99 -18
- package/lib/compiler/schemalBuilder.js +1 -1
- package/lib/data/i18n.js +5 -0
- package/lib/store/CascadeStore.d.ts +9 -1
- package/lib/store/CascadeStore.js +252 -267
- package/lib/store/IntrinsicCheckers.js +4 -3
- package/lib/store/TriggerExecutor.js +12 -6
- package/lib/store/checker.js +21 -6
- package/lib/types/EntityDesc.d.ts +1 -1
- package/lib/types/Exception.js +3 -1
- package/lib/types/Trigger.d.ts +6 -6
- package/lib/utils/validator.js +2 -2
- package/package.json +1 -1
|
@@ -58,8 +58,10 @@ export type OpSortAttr = Partial<{
|
|
|
58
58
|
$$seq$$: number;
|
|
59
59
|
$$updateAt$$: number;
|
|
60
60
|
action: number;
|
|
61
|
+
operatorId: number;
|
|
61
62
|
targetEntity: number;
|
|
62
63
|
bornAt: number;
|
|
64
|
+
logId: number;
|
|
63
65
|
iState: number;
|
|
64
66
|
[k: string]: any;
|
|
65
67
|
} | ExprOp<OpAttr | string>>;
|
|
@@ -35,6 +35,9 @@ export type OpSortAttr = Partial<{
|
|
|
35
35
|
$$createAt$$: number;
|
|
36
36
|
$$seq$$: number;
|
|
37
37
|
$$updateAt$$: number;
|
|
38
|
+
sourceRelationId: number;
|
|
39
|
+
pathId: number;
|
|
40
|
+
destRelationId: number;
|
|
38
41
|
[k: string]: any;
|
|
39
42
|
} | ExprOp<OpAttr | string>>;
|
|
40
43
|
export type OpAction = OakMakeAction<GenericAction | string>;
|
|
@@ -42,7 +42,11 @@ export type OpSortAttr = Partial<{
|
|
|
42
42
|
$$createAt$$: number;
|
|
43
43
|
$$seq$$: number;
|
|
44
44
|
$$updateAt$$: number;
|
|
45
|
+
uegId: number;
|
|
46
|
+
userId: number;
|
|
47
|
+
relationId: number;
|
|
45
48
|
claimEntityId: number;
|
|
49
|
+
userRelationId: number;
|
|
46
50
|
[k: string]: any;
|
|
47
51
|
} | ExprOp<OpAttr | string>>;
|
|
48
52
|
export type OpAction = OakMakeAction<GenericAction | string>;
|
|
@@ -865,69 +865,150 @@ function buildDependency(rebuild) {
|
|
|
865
865
|
];
|
|
866
866
|
const program = ts.createProgram(templateFileList, {});
|
|
867
867
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
868
|
+
let output = true;
|
|
868
869
|
if (!isModule) {
|
|
869
870
|
const initDevFile = join(cwd, 'src', 'initialize.frontend.ts');
|
|
870
|
-
if ((0, fs_1.existsSync)(initDevFile)
|
|
871
|
-
|
|
871
|
+
if ((0, fs_1.existsSync)(initDevFile)) {
|
|
872
|
+
let tips = `[${initDevFile}]文件已经存在`;
|
|
873
|
+
if (!rebuild) {
|
|
874
|
+
tips += ',忽略构建。';
|
|
875
|
+
output = false;
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
tips += ',将重新构建';
|
|
879
|
+
}
|
|
880
|
+
console.log(tips);
|
|
872
881
|
}
|
|
873
|
-
|
|
882
|
+
if (output) {
|
|
874
883
|
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer, initDevFile);
|
|
875
884
|
}
|
|
885
|
+
output = true;
|
|
876
886
|
const initProdFile = join(cwd, 'src', 'initialize.server.ts');
|
|
877
887
|
if ((0, fs_1.existsSync)(initProdFile) && !rebuild) {
|
|
878
|
-
|
|
888
|
+
let tips = `[${initProdFile}]文件已经存在`;
|
|
889
|
+
if (!rebuild) {
|
|
890
|
+
tips += ',忽略构建。';
|
|
891
|
+
output = false;
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
tips += ',将重新构建';
|
|
895
|
+
}
|
|
896
|
+
console.log(tips);
|
|
879
897
|
}
|
|
880
|
-
|
|
898
|
+
if (output) {
|
|
881
899
|
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer, initProdFile);
|
|
882
900
|
}
|
|
901
|
+
output = true;
|
|
883
902
|
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
|
|
884
903
|
if ((0, fs_1.existsSync)(initFeaturesFile) && !rebuild) {
|
|
885
|
-
|
|
904
|
+
let tips = `[${initFeaturesFile}]文件已经存在`;
|
|
905
|
+
if (!rebuild) {
|
|
906
|
+
tips += ',忽略构建。';
|
|
907
|
+
output = false;
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
tips += ',将重新构建,注意如果自定义过初始化过程,需要重新输入';
|
|
911
|
+
}
|
|
912
|
+
console.log(tips);
|
|
886
913
|
}
|
|
887
|
-
|
|
914
|
+
if (output) {
|
|
888
915
|
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer, initFeaturesFile);
|
|
889
916
|
}
|
|
890
917
|
}
|
|
918
|
+
output = true;
|
|
891
919
|
const runtimeCxtFile = join(cwd, 'src', 'types', 'RuntimeCxt.ts');
|
|
892
920
|
if ((0, fs_1.existsSync)(runtimeCxtFile) && !rebuild) {
|
|
893
|
-
|
|
921
|
+
let tips = `[${runtimeCxtFile}]文件已经存在`;
|
|
922
|
+
if (!rebuild) {
|
|
923
|
+
tips += ',忽略构建。';
|
|
924
|
+
output = false;
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
tips += ',将重新构建';
|
|
928
|
+
}
|
|
929
|
+
console.log(tips);
|
|
894
930
|
}
|
|
895
|
-
|
|
931
|
+
if (output) {
|
|
896
932
|
outputRuntimeCxt(dependencies, briefNames, program.getSourceFile(templateFileList[3]), printer, runtimeCxtFile);
|
|
897
933
|
}
|
|
934
|
+
output = true;
|
|
898
935
|
const dependentExceptionsFile = join(cwd, 'src', 'types', 'DependentExceptions.ts');
|
|
899
936
|
if ((0, fs_1.existsSync)(dependentExceptionsFile) && !rebuild) {
|
|
900
|
-
|
|
937
|
+
let tips = `[${dependentExceptionsFile}]文件已经存在`;
|
|
938
|
+
if (!rebuild) {
|
|
939
|
+
tips += ',忽略构建。';
|
|
940
|
+
output = false;
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
tips += ',将重新构建';
|
|
944
|
+
}
|
|
945
|
+
console.log(tips);
|
|
901
946
|
}
|
|
902
|
-
|
|
947
|
+
if (output) {
|
|
903
948
|
outputDependentExceptions(dependencies, briefNames, program.getSourceFile(templateFileList[4]), printer, dependentExceptionsFile);
|
|
904
949
|
}
|
|
950
|
+
output = true;
|
|
905
951
|
const polyfillDtsFile = join(cwd, 'typings', 'polyfill.d.ts');
|
|
906
952
|
if ((0, fs_1.existsSync)(polyfillDtsFile) && !rebuild) {
|
|
907
|
-
|
|
953
|
+
let tips = `[${polyfillDtsFile}]文件已经存在`;
|
|
954
|
+
if (!rebuild) {
|
|
955
|
+
tips += ',忽略构建。';
|
|
956
|
+
output = false;
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
tips += ',将重新构建';
|
|
960
|
+
}
|
|
961
|
+
console.log(tips);
|
|
908
962
|
}
|
|
909
|
-
|
|
963
|
+
if (output) {
|
|
910
964
|
outputPolyfillDts(dependencies, briefNames, program.getSourceFile(templateFileList[5]), printer, polyfillDtsFile);
|
|
911
965
|
}
|
|
966
|
+
output = true;
|
|
912
967
|
const featureIndexFile = join(cwd, 'src', 'features', 'index.ts');
|
|
913
968
|
if ((0, fs_1.existsSync)(featureIndexFile) && !rebuild) {
|
|
914
|
-
|
|
969
|
+
let tips = `[${featureIndexFile}]文件已经存在`;
|
|
970
|
+
if (!rebuild) {
|
|
971
|
+
tips += ',忽略构建。';
|
|
972
|
+
output = false;
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
tips += ',将重新构建';
|
|
976
|
+
}
|
|
977
|
+
console.log(tips);
|
|
915
978
|
}
|
|
916
979
|
else {
|
|
917
980
|
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6]), printer, featureIndexFile, isModule);
|
|
918
981
|
}
|
|
982
|
+
output = true;
|
|
919
983
|
let contextFile = join(cwd, 'src', 'context', 'BackendRuntimeContext.ts');
|
|
920
984
|
if ((0, fs_1.existsSync)(contextFile) && !rebuild) {
|
|
921
|
-
|
|
985
|
+
let tips = `[${contextFile}]文件已经存在`;
|
|
986
|
+
if (!rebuild) {
|
|
987
|
+
tips += ',忽略构建。';
|
|
988
|
+
output = false;
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
tips += ',将重新构建,注意如果自定义过上下文的其它方法或属性,需要重新输入';
|
|
992
|
+
}
|
|
993
|
+
console.log(tips);
|
|
922
994
|
}
|
|
923
|
-
|
|
995
|
+
if (output) {
|
|
924
996
|
outputContext(depGraph, program.getSourceFile(templateFileList[7]), printer, contextFile);
|
|
925
997
|
}
|
|
998
|
+
output = true;
|
|
926
999
|
contextFile = join(cwd, 'src', 'context', 'FrontendRuntimeContext.ts');
|
|
927
1000
|
if ((0, fs_1.existsSync)(contextFile) && !rebuild) {
|
|
928
|
-
|
|
1001
|
+
let tips = `[${contextFile}]文件已经存在`;
|
|
1002
|
+
if (!rebuild) {
|
|
1003
|
+
tips += ',忽略构建。';
|
|
1004
|
+
output = false;
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
tips += ',将重新构建,注意如果自定义过上下文的其它方法或属性,需要重新输入';
|
|
1008
|
+
}
|
|
1009
|
+
console.log(tips);
|
|
929
1010
|
}
|
|
930
|
-
|
|
1011
|
+
if (output) {
|
|
931
1012
|
outputContext(depGraph, program.getSourceFile(templateFileList[8]), printer, contextFile);
|
|
932
1013
|
}
|
|
933
1014
|
// 把各个依赖项目的一些初始化的文件拷贝过去
|
|
@@ -2532,7 +2532,7 @@ function _constructOpSortAttr(statements, entity) {
|
|
|
2532
2532
|
const text2 = text === 'Schema' ? entity : text;
|
|
2533
2533
|
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
|
|
2534
2534
|
if (manyToOneItem) {
|
|
2535
|
-
factory.createPropertySignature(undefined, factory.createIdentifier(`${name.text}Id`), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
|
|
2535
|
+
members.push(factory.createPropertySignature(undefined, factory.createIdentifier(`${name.text}Id`), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)));
|
|
2536
2536
|
}
|
|
2537
2537
|
else if (!['Object'].includes(text)) {
|
|
2538
2538
|
// todo 对State的专门处理
|
package/lib/data/i18n.js
CHANGED
|
@@ -16,6 +16,11 @@ const i18ns = [
|
|
|
16
16
|
"signatureFailed": "验签失败",
|
|
17
17
|
"attributesNull": "属性[%{attributes}]不允许为空",
|
|
18
18
|
"attributesCantUpdate": "属性[%{attributes}]不能更新",
|
|
19
|
+
"attributesFormatError": "属性[%{attributes}]格式不对,应当是[%{format}]",
|
|
20
|
+
"attributesTooLong": "属性[%{attributes}]长度过长,最长长度是[%{length}]",
|
|
21
|
+
"attributesOverMax": "属性[%{attributes}]超过最大值[%{threshold}]",
|
|
22
|
+
"attributesUnderMin": "属性[%{attributes}]低于最小值[%{threshold}]",
|
|
23
|
+
"attributesNotInEnumeration": "属性[%{attributes}]不在有效的枚举值当中",
|
|
19
24
|
"operationUnpermitted": "用户操作权限不足",
|
|
20
25
|
"dataInvisible": "用户查询权限不足",
|
|
21
26
|
"unLoggedIn": "用户未登录",
|
|
@@ -4,6 +4,15 @@ import { OperationRewriter, RowStore, SelectionRewriter } from '../types/RowStor
|
|
|
4
4
|
import { StorageSchema } from '../types/Storage';
|
|
5
5
|
import { SyncContext } from "./SyncRowStore";
|
|
6
6
|
import { AsyncContext } from "./AsyncRowStore";
|
|
7
|
+
/**
|
|
8
|
+
* 补全一个不完整的Selection,会将需要的各种连接、过滤和排序的属性加到相应的projection上
|
|
9
|
+
* @param schema
|
|
10
|
+
* @param entity
|
|
11
|
+
* @param selection
|
|
12
|
+
* @param context
|
|
13
|
+
* @param option
|
|
14
|
+
*/
|
|
15
|
+
export declare function polishSelection<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, Op extends SelectOption>(schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection'] | ED[keyof ED]['Aggregation'], context?: Cxt, option?: Op): void;
|
|
7
16
|
/**这个用来处理级联的select和update,对不同能力的 */
|
|
8
17
|
export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> extends RowStore<ED> {
|
|
9
18
|
constructor(storageSchema: StorageSchema<ED>);
|
|
@@ -13,7 +22,6 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
|
|
13
22
|
private operationRewriters;
|
|
14
23
|
private reinforceSelectionAsync;
|
|
15
24
|
private reinforceSelectionSync;
|
|
16
|
-
private reinforceSelectionInner;
|
|
17
25
|
private reinforceOperation;
|
|
18
26
|
registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED> | SyncContext<ED>, OperateOption>): void;
|
|
19
27
|
registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED> | SyncContext<ED>, SelectOption>): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CascadeStore = void 0;
|
|
3
|
+
exports.CascadeStore = exports.polishSelection = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
6
|
const Entity_1 = require("../types/Entity");
|
|
@@ -14,314 +14,299 @@ const filter_2 = require("./filter");
|
|
|
14
14
|
const uuid_1 = require("../utils/uuid");
|
|
15
15
|
const entities_1 = require("../compiler/entities");
|
|
16
16
|
const projection_1 = require("../utils/projection");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!isAggr && !selection.distinct) {
|
|
35
|
-
this.reinforceSelectionInner(entity, selection, context, option);
|
|
36
|
-
}
|
|
37
|
-
this.selectionRewriters.forEach(ele => {
|
|
38
|
-
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
|
39
|
-
(0, assert_1.default)(!(result instanceof Promise));
|
|
17
|
+
/**
|
|
18
|
+
* 补全一个不完整的Selection,会将需要的各种连接、过滤和排序的属性加到相应的projection上
|
|
19
|
+
* @param schema
|
|
20
|
+
* @param entity
|
|
21
|
+
* @param selection
|
|
22
|
+
* @param context
|
|
23
|
+
* @param option
|
|
24
|
+
*/
|
|
25
|
+
function polishSelection(schema, entity, selection, context, option) {
|
|
26
|
+
const { filter, data, sorter } = selection;
|
|
27
|
+
const assignNecessaryProjectionAttrs = (projectionNode, attrs) => {
|
|
28
|
+
attrs.forEach((attr) => {
|
|
29
|
+
if (!projectionNode.hasOwnProperty(attr)) {
|
|
30
|
+
Object.assign(projectionNode, {
|
|
31
|
+
[attr]: 1,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
40
34
|
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
const checkFilterNode = (entity2, filterNode, projectionNode, toBeAssignNode, filterNodeDict) => {
|
|
54
|
-
const necessaryAttrs = ['id'];
|
|
55
|
-
for (const attr in filterNode) {
|
|
56
|
-
if (attr === '#id') {
|
|
57
|
-
(0, assert_1.default)(!filterNodeDict[filterNode[attr]], `projection中结点的id有重复, ${filterNode[attr]}`);
|
|
58
|
-
Object.assign(filterNodeDict, {
|
|
59
|
-
[filterNode[attr]]: projectionNode,
|
|
60
|
-
});
|
|
61
|
-
if (toBeAssignNode[filterNode[attr]]) {
|
|
62
|
-
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode[filterNode[attr]]);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
else if (['$and', '$or'].includes(attr)) {
|
|
66
|
-
for (const node of filterNode[attr]) {
|
|
67
|
-
checkFilterNode(entity2, node, projectionNode, toBeAssignNode, filterNodeDict);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else if (attr === '$not') {
|
|
71
|
-
checkFilterNode(entity2, filterNode[attr], projectionNode, toBeAssignNode, filterNodeDict);
|
|
35
|
+
};
|
|
36
|
+
const checkFilterNode = (entity2, filterNode, projectionNode, toBeAssignNode, filterNodeDict) => {
|
|
37
|
+
const necessaryAttrs = ['id'];
|
|
38
|
+
for (const attr in filterNode) {
|
|
39
|
+
if (attr === '#id') {
|
|
40
|
+
(0, assert_1.default)(!filterNodeDict[filterNode[attr]], `projection中结点的id有重复, ${filterNode[attr]}`);
|
|
41
|
+
Object.assign(filterNodeDict, {
|
|
42
|
+
[filterNode[attr]]: projectionNode,
|
|
43
|
+
});
|
|
44
|
+
if (toBeAssignNode[filterNode[attr]]) {
|
|
45
|
+
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode[filterNode[attr]]);
|
|
72
46
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const { attributes } = fulltextIndex;
|
|
78
|
-
necessaryAttrs.push(...(attributes.map(ele => ele.name)));
|
|
47
|
+
}
|
|
48
|
+
else if (['$and', '$or'].includes(attr)) {
|
|
49
|
+
for (const node of filterNode[attr]) {
|
|
50
|
+
checkFilterNode(entity2, node, projectionNode, toBeAssignNode, filterNodeDict);
|
|
79
51
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
52
|
+
}
|
|
53
|
+
else if (attr === '$not') {
|
|
54
|
+
checkFilterNode(entity2, filterNode[attr], projectionNode, toBeAssignNode, filterNodeDict);
|
|
55
|
+
}
|
|
56
|
+
else if (attr === '$text') {
|
|
57
|
+
// 全文检索首先要有fulltext索引,其次要把fulltext的相关属性加到projection里
|
|
58
|
+
const { indexes } = schema[entity2];
|
|
59
|
+
const fulltextIndex = indexes.find(ele => ele.config && ele.config.type === 'fulltext');
|
|
60
|
+
const { attributes } = fulltextIndex;
|
|
61
|
+
necessaryAttrs.push(...(attributes.map(ele => ele.name)));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
65
|
+
const exprResult = (0, types_1.getAttrRefInExpression)(filterNode[attr]);
|
|
66
|
+
for (const nodeName in exprResult) {
|
|
67
|
+
if (nodeName === '#current') {
|
|
68
|
+
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
100
69
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const rel = this.judgeRelation(entity2, attr);
|
|
104
|
-
if (rel === 1) {
|
|
105
|
-
necessaryAttrs.push(attr);
|
|
70
|
+
else if (filterNodeDict[nodeName]) {
|
|
71
|
+
assignNecessaryProjectionAttrs(filterNodeDict[nodeName], exprResult[nodeName]);
|
|
106
72
|
}
|
|
107
|
-
else
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!projectionNode[attr]) {
|
|
111
|
-
Object.assign(projectionNode, {
|
|
112
|
-
[attr]: {
|
|
113
|
-
id: 1,
|
|
114
|
-
}
|
|
115
|
-
});
|
|
73
|
+
else {
|
|
74
|
+
if (toBeAssignNode[nodeName]) {
|
|
75
|
+
toBeAssignNode[nodeName].push(...exprResult[nodeName]);
|
|
116
76
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
necessaryAttrs.push(`${attr}Id`);
|
|
121
|
-
if (!projectionNode[attr]) {
|
|
122
|
-
Object.assign(projectionNode, {
|
|
123
|
-
[attr]: {
|
|
124
|
-
id: 1,
|
|
125
|
-
}
|
|
77
|
+
else {
|
|
78
|
+
Object.assign(toBeAssignNode, {
|
|
79
|
+
[nodeName]: exprResult[nodeName],
|
|
126
80
|
});
|
|
127
81
|
}
|
|
128
|
-
checkFilterNode(rel, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
129
|
-
}
|
|
130
|
-
else if (rel instanceof Array) {
|
|
131
|
-
// 子查询,暂时不处理
|
|
132
82
|
}
|
|
133
83
|
}
|
|
134
84
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const checkSorterNode = (entity2, sorterNode, projectionNode) => {
|
|
139
|
-
const checkSortAttr = (e2, sortAttr, projNode) => {
|
|
140
|
-
const necessaryAttrs = [];
|
|
141
|
-
for (const attr in sortAttr) {
|
|
142
|
-
const rel = this.judgeRelation(e2, attr);
|
|
143
|
-
if (typeof rel === 'number' && [0, 1].includes(rel)) {
|
|
85
|
+
else {
|
|
86
|
+
const rel = (0, relation_1.judgeRelation)(schema, entity2, attr);
|
|
87
|
+
if (rel === 1) {
|
|
144
88
|
necessaryAttrs.push(attr);
|
|
145
89
|
}
|
|
146
|
-
else if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
90
|
+
else if (rel === 2) {
|
|
91
|
+
// entity/entityId反指
|
|
92
|
+
necessaryAttrs.push('entity', 'entityId');
|
|
93
|
+
if (!projectionNode[attr]) {
|
|
94
|
+
Object.assign(projectionNode, {
|
|
95
|
+
[attr]: {
|
|
96
|
+
id: 1,
|
|
97
|
+
}
|
|
150
98
|
});
|
|
151
99
|
}
|
|
152
|
-
(
|
|
153
|
-
checkSortAttr(rel, sortAttr[attr], projNode[attr]);
|
|
100
|
+
checkFilterNode(attr, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
154
101
|
}
|
|
155
|
-
else {
|
|
156
|
-
|
|
157
|
-
if (!
|
|
158
|
-
Object.assign(
|
|
159
|
-
[attr]: {
|
|
102
|
+
else if (typeof rel === 'string') {
|
|
103
|
+
necessaryAttrs.push(`${attr}Id`);
|
|
104
|
+
if (!projectionNode[attr]) {
|
|
105
|
+
Object.assign(projectionNode, {
|
|
106
|
+
[attr]: {
|
|
107
|
+
id: 1,
|
|
108
|
+
}
|
|
160
109
|
});
|
|
161
110
|
}
|
|
162
|
-
(
|
|
163
|
-
|
|
111
|
+
checkFilterNode(rel, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
112
|
+
}
|
|
113
|
+
else if (rel instanceof Array) {
|
|
114
|
+
// 子查询,暂时不处理
|
|
164
115
|
}
|
|
165
116
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
sorterNode.forEach((node) => {
|
|
169
|
-
const { $attr } = node;
|
|
170
|
-
checkSortAttr(entity2, $attr, projectionNode);
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
let relevantIds = [];
|
|
174
|
-
if (filter) {
|
|
175
|
-
const toBeAssignNode = {}; // 用来记录在表达式中涉及到的结点
|
|
176
|
-
// filter当中所关联到的属性必须在projection中
|
|
177
|
-
const filterNodeDict = {};
|
|
178
|
-
checkFilterNode(entity, filter, data, toBeAssignNode, filterNodeDict);
|
|
179
|
-
relevantIds = (0, filter_2.getRelevantIds)(filter);
|
|
180
|
-
}
|
|
181
|
-
// sorter也得取了,前端需要处理排序
|
|
182
|
-
if (sorter) {
|
|
183
|
-
checkSorterNode(entity, sorter, data);
|
|
117
|
+
}
|
|
118
|
+
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
184
119
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
const necessaryAttrs = [
|
|
189
|
-
for (const attr in
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
120
|
+
};
|
|
121
|
+
const checkSorterNode = (entity2, sorterNode, projectionNode) => {
|
|
122
|
+
const checkSortAttr = (e2, sortAttr, projNode) => {
|
|
123
|
+
const necessaryAttrs = [];
|
|
124
|
+
for (const attr in sortAttr) {
|
|
125
|
+
const rel = (0, relation_1.judgeRelation)(schema, e2, attr);
|
|
126
|
+
if (typeof rel === 'number' && [0, 1].includes(rel)) {
|
|
127
|
+
necessaryAttrs.push(attr);
|
|
128
|
+
}
|
|
129
|
+
else if (typeof rel === 'string') {
|
|
130
|
+
if (!projNode[attr]) {
|
|
131
|
+
Object.assign(projNode, {
|
|
132
|
+
[attr]: {},
|
|
133
|
+
});
|
|
197
134
|
}
|
|
135
|
+
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
136
|
+
checkSortAttr(rel, sortAttr[attr], projNode[attr]);
|
|
198
137
|
}
|
|
199
138
|
else {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
else if (projectionNodeDict[nodeName]) {
|
|
207
|
-
assignNecessaryProjectionAttrs(projectionNodeDict[nodeName], exprResult[nodeName]);
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
if (toBeAssignNode2[nodeName]) {
|
|
211
|
-
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
Object.assign(toBeAssignNode2, {
|
|
215
|
-
[nodeName]: exprResult[nodeName],
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
139
|
+
(0, assert_1.default)(rel === 2);
|
|
140
|
+
if (!projNode[attr]) {
|
|
141
|
+
Object.assign(projNode, {
|
|
142
|
+
[attr]: {},
|
|
143
|
+
});
|
|
220
144
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
145
|
+
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
146
|
+
checkSortAttr(attr, sortAttr[attr], projNode[attr]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
assignNecessaryProjectionAttrs(projNode, necessaryAttrs);
|
|
150
|
+
};
|
|
151
|
+
sorterNode.forEach((node) => {
|
|
152
|
+
const { $attr } = node;
|
|
153
|
+
checkSortAttr(entity2, $attr, projectionNode);
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
let relevantIds = [];
|
|
157
|
+
if (filter) {
|
|
158
|
+
const toBeAssignNode = {}; // 用来记录在表达式中涉及到的结点
|
|
159
|
+
// filter当中所关联到的属性必须在projection中
|
|
160
|
+
const filterNodeDict = {};
|
|
161
|
+
checkFilterNode(entity, filter, data, toBeAssignNode, filterNodeDict);
|
|
162
|
+
relevantIds = (0, filter_2.getRelevantIds)(filter);
|
|
163
|
+
}
|
|
164
|
+
// sorter也得取了,前端需要处理排序
|
|
165
|
+
if (sorter) {
|
|
166
|
+
checkSorterNode(entity, sorter, data);
|
|
167
|
+
}
|
|
168
|
+
const toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
|
|
169
|
+
const projectionNodeDict = {};
|
|
170
|
+
const checkProjectionNode = (entity2, projectionNode) => {
|
|
171
|
+
const necessaryAttrs = ['id', '$$createAt$$', '$$updateAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
|
172
|
+
for (const attr in projectionNode) {
|
|
173
|
+
if (attr === '#id') {
|
|
174
|
+
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], `projection中结点的id有重复, ${projectionNode[attr]}`);
|
|
175
|
+
Object.assign(projectionNodeDict, {
|
|
176
|
+
[projectionNode[attr]]: projectionNode,
|
|
177
|
+
});
|
|
178
|
+
if (toBeAssignNode2[projectionNode[attr]]) {
|
|
179
|
+
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode2[projectionNode[attr]]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
184
|
+
const exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
|
|
185
|
+
for (const nodeName in exprResult) {
|
|
186
|
+
if (nodeName === '#current') {
|
|
187
|
+
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
230
188
|
}
|
|
231
|
-
else if (
|
|
232
|
-
|
|
233
|
-
checkProjectionNode(rel, projectionNode[attr]);
|
|
189
|
+
else if (projectionNodeDict[nodeName]) {
|
|
190
|
+
assignNecessaryProjectionAttrs(projectionNodeDict[nodeName], exprResult[nodeName]);
|
|
234
191
|
}
|
|
235
|
-
else
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
assignNecessaryProjectionAttrs(data, [rel[1]]);
|
|
192
|
+
else {
|
|
193
|
+
if (toBeAssignNode2[nodeName]) {
|
|
194
|
+
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
|
|
239
195
|
}
|
|
240
196
|
else {
|
|
241
|
-
|
|
197
|
+
Object.assign(toBeAssignNode2, {
|
|
198
|
+
[nodeName]: exprResult[nodeName],
|
|
199
|
+
});
|
|
242
200
|
}
|
|
243
|
-
this.reinforceSelectionInner(rel[0], projectionNode[attr], context, option);
|
|
244
201
|
}
|
|
245
202
|
}
|
|
246
203
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
/* if (this.getSchema()[entity2].toModi) {
|
|
252
|
-
Object.assign(projectionNode, {
|
|
253
|
-
modi$entity: {
|
|
254
|
-
$entity: 'modi',
|
|
255
|
-
data: {
|
|
256
|
-
id: 1,
|
|
257
|
-
targetEntity: 1,
|
|
258
|
-
entity: 1,
|
|
259
|
-
entityId: 1,
|
|
260
|
-
action: 1,
|
|
261
|
-
iState: 1,
|
|
262
|
-
data: 1,
|
|
263
|
-
filter: 1,
|
|
264
|
-
$$createAt$$: 1,
|
|
265
|
-
$$updateAt$$: 1,
|
|
266
|
-
},
|
|
267
|
-
filter: {
|
|
268
|
-
iState: 'active',
|
|
269
|
-
},
|
|
204
|
+
else {
|
|
205
|
+
const rel = (0, relation_1.judgeRelation)(schema, entity2, attr);
|
|
206
|
+
if (rel === 1) {
|
|
207
|
+
necessaryAttrs.push(attr);
|
|
270
208
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
209
|
+
else if (rel === 2) {
|
|
210
|
+
// entity/entityId反指
|
|
211
|
+
necessaryAttrs.push('entity', 'entityId');
|
|
212
|
+
checkProjectionNode(attr, projectionNode[attr]);
|
|
213
|
+
}
|
|
214
|
+
else if (typeof rel === 'string') {
|
|
215
|
+
necessaryAttrs.push(`${attr}Id`);
|
|
216
|
+
checkProjectionNode(rel, projectionNode[attr]);
|
|
217
|
+
}
|
|
218
|
+
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
|
|
219
|
+
const { data, filter } = projectionNode[attr];
|
|
220
|
+
if (rel[1]) {
|
|
221
|
+
assignNecessaryProjectionAttrs(data, [rel[1]]);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
assignNecessaryProjectionAttrs(data, ['entity', 'entityId']);
|
|
225
|
+
}
|
|
226
|
+
polishSelection(schema, rel[0], projectionNode[attr], context, option);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
231
|
+
}
|
|
232
|
+
// 如果对象上有relation关系,在此将本用户相关的relation和actionAuth全部取出
|
|
233
|
+
// 还要将actionAuth上没有relation关系但destEntity为本对象的行也全部取出,这些是指向userId的可能路径
|
|
234
|
+
// 放在这里有点怪异,暂先这样
|
|
235
|
+
if (context && !option?.dontCollect) {
|
|
236
|
+
const userId = context.getCurrentUserId(true);
|
|
237
|
+
if (userId && !entities_1.SYSTEM_RESERVE_ENTITIES.includes(entity2)) {
|
|
238
|
+
if (schema[entity2].relation && !projectionNode.userRelation$entity) {
|
|
239
|
+
Object.assign(projectionNode, {
|
|
240
|
+
userRelation$entity: {
|
|
241
|
+
$entity: 'userRelation',
|
|
242
|
+
data: {
|
|
243
|
+
id: 1,
|
|
244
|
+
entity: 1,
|
|
245
|
+
entityId: 1,
|
|
246
|
+
userId: 1,
|
|
247
|
+
relationId: 1,
|
|
248
|
+
relation: {
|
|
284
249
|
id: 1,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
userId: 1,
|
|
288
|
-
relationId: 1,
|
|
289
|
-
relation: {
|
|
290
|
-
id: 1,
|
|
291
|
-
name: 1,
|
|
292
|
-
display: 1,
|
|
293
|
-
[Entity_1.CreateAtAttribute]: 1,
|
|
294
|
-
[Entity_1.UpdateAtAttribute]: 1,
|
|
295
|
-
},
|
|
250
|
+
name: 1,
|
|
251
|
+
display: 1,
|
|
296
252
|
[Entity_1.CreateAtAttribute]: 1,
|
|
297
253
|
[Entity_1.UpdateAtAttribute]: 1,
|
|
298
254
|
},
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
},
|
|
255
|
+
[Entity_1.CreateAtAttribute]: 1,
|
|
256
|
+
[Entity_1.UpdateAtAttribute]: 1,
|
|
302
257
|
},
|
|
303
|
-
|
|
304
|
-
|
|
258
|
+
filter: {
|
|
259
|
+
userId,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
});
|
|
305
263
|
}
|
|
306
264
|
}
|
|
307
|
-
};
|
|
308
|
-
checkProjectionNode(entity, data);
|
|
309
|
-
if (!sorter && relevantIds.length === 0) {
|
|
310
|
-
// 如果没有sorter,就给予一个按createAt逆序的sorter
|
|
311
|
-
Object.assign(selection, {
|
|
312
|
-
sorter: [
|
|
313
|
-
{
|
|
314
|
-
$attr: {
|
|
315
|
-
$$createAt$$: 1,
|
|
316
|
-
},
|
|
317
|
-
$direction: 'desc',
|
|
318
|
-
}
|
|
319
|
-
]
|
|
320
|
-
});
|
|
321
|
-
Object.assign(data, {
|
|
322
|
-
$$createAt$$: 1,
|
|
323
|
-
});
|
|
324
265
|
}
|
|
266
|
+
};
|
|
267
|
+
checkProjectionNode(entity, data);
|
|
268
|
+
if (!sorter && relevantIds.length === 0) {
|
|
269
|
+
// 如果没有sorter,就给予一个按createAt逆序的sorter
|
|
270
|
+
Object.assign(selection, {
|
|
271
|
+
sorter: [
|
|
272
|
+
{
|
|
273
|
+
$attr: {
|
|
274
|
+
$$createAt$$: 1,
|
|
275
|
+
},
|
|
276
|
+
$direction: 'desc',
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
});
|
|
280
|
+
Object.assign(data, {
|
|
281
|
+
$$createAt$$: 1,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
exports.polishSelection = polishSelection;
|
|
286
|
+
/**这个用来处理级联的select和update,对不同能力的 */
|
|
287
|
+
class CascadeStore extends RowStore_1.RowStore {
|
|
288
|
+
constructor(storageSchema) {
|
|
289
|
+
super(storageSchema);
|
|
290
|
+
}
|
|
291
|
+
selectionRewriters = [];
|
|
292
|
+
operationRewriters = [];
|
|
293
|
+
async reinforceSelectionAsync(entity, selection, context, option, isAggr) {
|
|
294
|
+
if (!isAggr && !selection.distinct) {
|
|
295
|
+
polishSelection(this.getSchema(), entity, selection, context, option);
|
|
296
|
+
}
|
|
297
|
+
const rewriterPromises = this.selectionRewriters.map(ele => ele(this.getSchema(), entity, selection, context, option, isAggr));
|
|
298
|
+
if (rewriterPromises.length > 0) {
|
|
299
|
+
await Promise.all(rewriterPromises);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
reinforceSelectionSync(entity, selection, context, option, isAggr) {
|
|
303
|
+
if (!isAggr && !selection.distinct) {
|
|
304
|
+
polishSelection(this.getSchema(), entity, selection);
|
|
305
|
+
}
|
|
306
|
+
this.selectionRewriters.forEach(ele => {
|
|
307
|
+
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
|
308
|
+
(0, assert_1.default)(!(result instanceof Promise));
|
|
309
|
+
});
|
|
325
310
|
}
|
|
326
311
|
async reinforceOperation(entity, operation, context, option) {
|
|
327
312
|
await Promise.all(this.operationRewriters.map(ele => ele(this.getSchema(), entity, operation, context, option)));
|
|
@@ -273,6 +273,7 @@ function cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, restA
|
|
|
273
273
|
if (!f) {
|
|
274
274
|
return true;
|
|
275
275
|
}
|
|
276
|
+
(0, assert_1.default)(typeof f !== 'function', '此处是原来写的时候的疏漏情况,跑出来再改。by Xc 20250214');
|
|
276
277
|
// 此时看应用了success的attributes更新后,能否消除掉f中的部分条件
|
|
277
278
|
const result = (0, filter_1.analyzeFilterRelation)(entity, schema, successAttrFilter, f, true);
|
|
278
279
|
if (typeof result === 'boolean') {
|
|
@@ -325,7 +326,7 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
|
|
|
325
326
|
const attrs = Object.keys(data);
|
|
326
327
|
const extras = (0, lodash_1.difference)(attrs, updateAttrs);
|
|
327
328
|
if (extras.length > 0) {
|
|
328
|
-
throw new types_1.OakAttrCantUpdateException(entity, extras
|
|
329
|
+
throw new types_1.OakAttrCantUpdateException(entity, extras);
|
|
329
330
|
}
|
|
330
331
|
const condition = attrs.map(ele => matrix[ele]);
|
|
331
332
|
const actions = condition.map(ele => ele?.actions).filter(ele => !!ele);
|
|
@@ -335,7 +336,7 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
|
|
|
335
336
|
if (!a.includes(action)) {
|
|
336
337
|
// 找到不满足的那个attr
|
|
337
338
|
const attrsIllegal = attrs.filter((attr) => matrix[attr]?.actions && !matrix[attr]?.actions?.includes(action));
|
|
338
|
-
throw new types_1.OakAttrCantUpdateException(entity, attrsIllegal
|
|
339
|
+
throw new types_1.OakAttrCantUpdateException(entity, attrsIllegal);
|
|
339
340
|
}
|
|
340
341
|
}
|
|
341
342
|
const filters = condition.map(ele => {
|
|
@@ -353,7 +354,7 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
|
|
|
353
354
|
if (attrs.length > 1) {
|
|
354
355
|
return cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, attrs, context);
|
|
355
356
|
}
|
|
356
|
-
throw new types_1.OakAttrCantUpdateException(entity, attrs
|
|
357
|
+
throw new types_1.OakAttrCantUpdateException(entity, attrs);
|
|
357
358
|
}
|
|
358
359
|
});
|
|
359
360
|
}
|
|
@@ -276,7 +276,8 @@ class TriggerExecutor {
|
|
|
276
276
|
// 加上modi的过滤条件
|
|
277
277
|
&& this.judgeModiTurn(option, trigger));
|
|
278
278
|
if (triggers) {
|
|
279
|
-
const preTriggers = triggers.filter(ele => ele.when === 'before' && (!ele.check || ele.check(operation))
|
|
279
|
+
const preTriggers = triggers.filter(ele => ele.when === 'before' && (!ele.check || ele.check(operation))
|
|
280
|
+
&& (!ele.attributes || (0, lodash_1.difference)(ele.attributes, Object.keys(operation.data)).length === 0));
|
|
280
281
|
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
281
282
|
(!ele.check || ele.check(operation)));
|
|
282
283
|
if (context instanceof SyncRowStore_1.SyncContext) {
|
|
@@ -363,14 +364,18 @@ class TriggerExecutor {
|
|
|
363
364
|
if (trigger.strict === 'makeSure' && ids.length && !cleanTriggerDataBySelf) {
|
|
364
365
|
// 这里开root模式,否则还可能有权限问题
|
|
365
366
|
const closeRoot2 = context.openRootMode();
|
|
367
|
+
const data = {
|
|
368
|
+
[Entity_1.TriggerDataAttribute]: null,
|
|
369
|
+
[Entity_1.TriggerUuidAttribute]: null,
|
|
370
|
+
};
|
|
371
|
+
if (typeof callback === 'object') {
|
|
372
|
+
Object.assign(data, callback);
|
|
373
|
+
}
|
|
366
374
|
try {
|
|
367
375
|
await context.operate(entity, {
|
|
368
376
|
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
369
377
|
action: 'update',
|
|
370
|
-
data
|
|
371
|
-
[Entity_1.TriggerDataAttribute]: null,
|
|
372
|
-
[Entity_1.TriggerUuidAttribute]: null,
|
|
373
|
-
},
|
|
378
|
+
data,
|
|
374
379
|
filter: {
|
|
375
380
|
id: {
|
|
376
381
|
$in: ids,
|
|
@@ -426,7 +431,8 @@ class TriggerExecutor {
|
|
|
426
431
|
// 加上modi的过滤条件
|
|
427
432
|
&& this.judgeModiTurn(option, trigger));
|
|
428
433
|
if (triggers) {
|
|
429
|
-
const postTriggers = triggers.filter(ele => ele.when === 'after' && (!ele.check || ele.check(operation))
|
|
434
|
+
const postTriggers = triggers.filter(ele => ele.when === 'after' && (!ele.check || ele.check(operation))
|
|
435
|
+
&& (!ele.attributes || (0, lodash_1.difference)(ele.attributes, Object.keys(operation.data)).length === 0));
|
|
430
436
|
const commitTriggers = triggers.filter(ele => ele.when === 'commit' &&
|
|
431
437
|
(!ele.check || ele.check(operation)));
|
|
432
438
|
if (context instanceof SyncRowStore_1.SyncContext) {
|
package/lib/store/checker.js
CHANGED
|
@@ -352,11 +352,17 @@ function checkAttributeLegal(schema, entity, data) {
|
|
|
352
352
|
case 'char':
|
|
353
353
|
case 'varchar': {
|
|
354
354
|
if (typeof data[attr] !== 'string') {
|
|
355
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
355
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesFormatError', 'oak-domain', {
|
|
356
|
+
attribtues: attr,
|
|
357
|
+
format: 'string',
|
|
358
|
+
});
|
|
356
359
|
}
|
|
357
360
|
const { length } = params;
|
|
358
361
|
if (length && data[attr].length > length) {
|
|
359
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
362
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesTooLong', 'oak-domain', {
|
|
363
|
+
attributes: attr,
|
|
364
|
+
length,
|
|
365
|
+
});
|
|
360
366
|
}
|
|
361
367
|
break;
|
|
362
368
|
}
|
|
@@ -367,21 +373,30 @@ function checkAttributeLegal(schema, entity, data) {
|
|
|
367
373
|
case 'decimal':
|
|
368
374
|
case 'money': {
|
|
369
375
|
if (typeof data[attr] !== 'number') {
|
|
370
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
376
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesFormatError', 'oak-domain', {
|
|
377
|
+
attribtues: attr,
|
|
378
|
+
format: 'number',
|
|
379
|
+
});
|
|
371
380
|
}
|
|
372
381
|
const { min, max } = params || {};
|
|
373
382
|
if (typeof min === 'number' && data[attr] < min) {
|
|
374
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
383
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesUnderMin', 'oak-domain', {
|
|
384
|
+
attributes: attr,
|
|
385
|
+
threshold: `${min}`,
|
|
386
|
+
});
|
|
375
387
|
}
|
|
376
388
|
if (typeof max === 'number' && data[attr] > max) {
|
|
377
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
389
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesOverMax', 'oak-domain', {
|
|
390
|
+
attributes: attr,
|
|
391
|
+
threshold: `${max}`,
|
|
392
|
+
});
|
|
378
393
|
}
|
|
379
394
|
break;
|
|
380
395
|
}
|
|
381
396
|
case 'enum': {
|
|
382
397
|
(0, assert_1.default)(enumeration);
|
|
383
398
|
if (!enumeration.includes(data[attr])) {
|
|
384
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr],
|
|
399
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'error::attributesNotInEnumeration');
|
|
385
400
|
}
|
|
386
401
|
break;
|
|
387
402
|
}
|
|
@@ -18,7 +18,7 @@ export type AttrUpdateMatrix<ED extends EntityDict> = {
|
|
|
18
18
|
[T in keyof ED]?: {
|
|
19
19
|
[A in keyof ED[T]['Update']['data']]?: {
|
|
20
20
|
actions?: ED[T]['Action'][];
|
|
21
|
-
filter?: (NonNullable<ED[T]['Filter']>) | ((action: ED[T]['Action']) =>
|
|
21
|
+
filter?: (NonNullable<ED[T]['Filter']>) | ((action: ED[T]['Action']) => ED[T]['Filter'] | undefined);
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
};
|
package/lib/types/Exception.js
CHANGED
|
@@ -225,7 +225,9 @@ class OakInputIllegalException extends OakUserException {
|
|
|
225
225
|
attributes;
|
|
226
226
|
entity;
|
|
227
227
|
constructor(entity, attributes, message, _module, params) {
|
|
228
|
-
super(message, _module, params
|
|
228
|
+
super(message, _module, params || {
|
|
229
|
+
attributes: attributes.join(','),
|
|
230
|
+
});
|
|
229
231
|
this.entity = entity;
|
|
230
232
|
this.attributes = attributes;
|
|
231
233
|
}
|
package/lib/types/Trigger.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export interface CreateTriggerInTxn<ED extends EntityDict & BaseEntityDict, T ex
|
|
|
36
36
|
operation: ED[T]['Create'];
|
|
37
37
|
}, context: Cxt, option: OperateOption) => Promise<number> | number;
|
|
38
38
|
}
|
|
39
|
-
interface TriggerCrossTxn<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>> {
|
|
39
|
+
interface TriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> {
|
|
40
40
|
when: 'commit';
|
|
41
41
|
strict?: 'takeEasy' | 'makeSure';
|
|
42
42
|
cs?: true;
|
|
@@ -45,9 +45,9 @@ interface TriggerCrossTxn<ED extends EntityDict & BaseEntityDict, Cxt extends As
|
|
|
45
45
|
grouped?: true;
|
|
46
46
|
fn: (event: {
|
|
47
47
|
ids: string[];
|
|
48
|
-
}, context: Cxt, option: OperateOption) => Promise<((context: Cxt, option: OperateOption) => Promise<any>) | void>;
|
|
48
|
+
}, context: Cxt, option: OperateOption) => Promise<((context: Cxt, option: OperateOption) => Promise<any>) | void | ED[T]['Update']['data']>;
|
|
49
49
|
}
|
|
50
|
-
export interface CreateTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends CreateTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, Cxt> {
|
|
50
|
+
export interface CreateTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends CreateTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, T, Cxt> {
|
|
51
51
|
}
|
|
52
52
|
export type CreateTrigger<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = CreateTriggerInTxn<ED, T, Cxt> | CreateTriggerCrossTxn<ED, T, Cxt>;
|
|
53
53
|
/**
|
|
@@ -57,7 +57,7 @@ export type CreateTrigger<ED extends EntityDict & BaseEntityDict, T extends keyo
|
|
|
57
57
|
*/
|
|
58
58
|
export interface UpdateTriggerBase<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends TriggerBase<ED, T> {
|
|
59
59
|
action: Exclude<ED[T]['Action'], ExcludeUpdateAction> | Array<Exclude<ED[T]['Action'], ExcludeUpdateAction>>;
|
|
60
|
-
attributes?:
|
|
60
|
+
attributes?: Array<keyof ED[T]['OpSchema']>;
|
|
61
61
|
mt?: ModiTurn;
|
|
62
62
|
check?: (operation: ED[T]['Update']) => boolean;
|
|
63
63
|
filter?: ED[T]['Filter'] | ((operation: ED[T]['Update'], context: Cxt, option: OperateOption) => ED[T]['Filter'] | Promise<ED[T]['Filter']>);
|
|
@@ -68,7 +68,7 @@ export interface UpdateTriggerInTxn<ED extends EntityDict & BaseEntityDict, T ex
|
|
|
68
68
|
operation: ED[T]['Update'];
|
|
69
69
|
}, context: Cxt, option: OperateOption) => Promise<number> | number;
|
|
70
70
|
}
|
|
71
|
-
export interface UpdateTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends UpdateTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, Cxt> {
|
|
71
|
+
export interface UpdateTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends UpdateTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, T, Cxt> {
|
|
72
72
|
}
|
|
73
73
|
export type UpdateTrigger<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = UpdateTriggerInTxn<ED, T, Cxt> | UpdateTriggerCrossTxn<ED, T, Cxt>;
|
|
74
74
|
/**
|
|
@@ -88,7 +88,7 @@ export interface RemoveTriggerInTxn<ED extends EntityDict & BaseEntityDict, T ex
|
|
|
88
88
|
operation: ED[T]['Remove'];
|
|
89
89
|
}, context: Cxt, option: OperateOption) => Promise<number> | number;
|
|
90
90
|
}
|
|
91
|
-
export interface RemoveTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, Cxt> {
|
|
91
|
+
export interface RemoveTriggerCrossTxn<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt>, TriggerCrossTxn<ED, T, Cxt> {
|
|
92
92
|
}
|
|
93
93
|
export type RemoveTrigger<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = RemoveTriggerInTxn<ED, T, Cxt> | RemoveTriggerCrossTxn<ED, T, Cxt>;
|
|
94
94
|
export interface SelectTriggerBase<ED extends EntityDict & BaseEntityDict, T extends keyof ED> extends TriggerBase<ED, T> {
|
package/lib/utils/validator.js
CHANGED
|
@@ -119,7 +119,7 @@ function checkAttributesNotNull(entity, data, attributes, allowEmpty) {
|
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
if (attrs.length > 0) {
|
|
122
|
-
throw new types_1.OakAttrNotNullException(entity, attrs, '
|
|
122
|
+
throw new types_1.OakAttrNotNullException(entity, attrs, 'error::attributesNull');
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
exports.checkAttributesNotNull = checkAttributesNotNull;
|
|
@@ -127,7 +127,7 @@ exports.checkAttributesNotNull = checkAttributesNotNull;
|
|
|
127
127
|
function checkAttributesScope(entity, data, attributes) {
|
|
128
128
|
const attrs = attributes.filter(attr => !data.hasOwnProperty(attr));
|
|
129
129
|
if (attrs.length > 0) {
|
|
130
|
-
throw new types_1.OakInputIllegalException(entity, attrs, '
|
|
130
|
+
throw new types_1.OakInputIllegalException(entity, attrs, 'error::attributesCantUpdate');
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
exports.checkAttributesScope = checkAttributesScope;
|