oak-domain 4.2.3 → 4.2.4
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 +8 -8
- package/lib/base-app-domain/Oper/Schema.d.ts +158 -158
- package/lib/base-app-domain/Oper/Storage.js +56 -56
- package/lib/base-app-domain/User/Action.d.ts +11 -11
- package/lib/base-app-domain/User/Action.js +12 -12
- package/lib/base-app-domain/UserEntityGrant/Action.d.ts +5 -5
- package/lib/base-app-domain/UserEntityGrant/Action.js +5 -5
- package/lib/compiler/schemalBuilder.js +4167 -4167
- package/lib/entities/Oper.d.ts +12 -12
- package/lib/entities/Oper.js +36 -36
- package/lib/entities/User.d.ts +18 -18
- package/lib/entities/User.js +32 -32
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -3
- package/lib/store/AsyncRowStore.d.ts +66 -66
- package/lib/store/CascadeStore.d.ts +109 -109
- package/lib/store/CascadeStore.js +1728 -1728
- package/lib/store/RelationAuth.js +1209 -1209
- package/lib/store/TriggerExecutor.js +468 -468
- package/lib/store/actionDef.js +278 -278
- package/lib/store/checker.js +487 -487
- package/lib/store/relation.d.ts +12 -12
- package/lib/store/relation.js +74 -74
- package/lib/triggers/index.d.ts +5 -5
- package/lib/triggers/index.js +28 -28
- package/lib/types/Configuration.d.ts +42 -42
- package/lib/types/Configuration.js +3 -3
- package/lib/types/Connector.d.ts +39 -39
- package/lib/types/Entity.d.ts +209 -209
- package/lib/types/Sync.d.ts +74 -68
- package/lib/types/Sync.js +9 -9
- package/lib/types/index.d.ts +27 -27
- package/lib/types/index.js +30 -30
- package/lib/utils/SimpleConnector.d.ts +81 -81
- package/lib/utils/SimpleConnector.js +217 -217
- package/lib/utils/assert.d.ts +5 -5
- package/lib/utils/projection.d.ts +4 -4
- package/lib/utils/relationPath.d.ts +31 -31
- package/lib/utils/relationPath.js +202 -202
- package/package.json +51 -51
- package/src/entities/ActionAuth.ts +41 -41
- package/src/entities/I18n.ts +45 -45
- package/src/entities/Modi.ts +69 -69
- package/src/entities/ModiEntity.ts +26 -26
- package/src/entities/Oper.ts +48 -48
- package/src/entities/OperEntity.ts +27 -27
- package/src/entities/Path.ts +43 -43
- package/src/entities/Relation.ts +43 -43
- package/src/entities/RelationAuth.ts +44 -44
- package/src/entities/User.ts +48 -48
- package/src/entities/UserEntityClaim.ts +29 -29
- package/src/entities/UserEntityGrant.ts +24 -24
- package/src/entities/UserRelation.ts +50 -50
package/lib/store/checker.js
CHANGED
|
@@ -1,487 +1,487 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createCreateCheckers = exports.createRemoveCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
-
const filter_1 = require("../store/filter");
|
|
7
|
-
const Exception_1 = require("../types/Exception");
|
|
8
|
-
const types_1 = require("../types");
|
|
9
|
-
const actionDef_1 = require("./actionDef");
|
|
10
|
-
const lodash_1 = require("../utils/lodash");
|
|
11
|
-
const action_1 = require("../actions/action");
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* @param checker 要翻译的checker
|
|
15
|
-
* @param silent 如果silent,则row和relation类型的checker只会把限制条件加到查询上,而不报错(除掉create动作)
|
|
16
|
-
* @returns
|
|
17
|
-
*/
|
|
18
|
-
function translateCheckerInAsyncContext(checker) {
|
|
19
|
-
const { entity, type } = checker;
|
|
20
|
-
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
|
21
|
-
switch (type) {
|
|
22
|
-
case 'data': {
|
|
23
|
-
const { checker: checkerFn } = checker;
|
|
24
|
-
const fn = (async ({ operation }, context) => {
|
|
25
|
-
const { data } = operation;
|
|
26
|
-
await checkerFn(data, context);
|
|
27
|
-
return 0;
|
|
28
|
-
});
|
|
29
|
-
return {
|
|
30
|
-
fn,
|
|
31
|
-
when,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
case 'row': {
|
|
35
|
-
const { filter, errMsg, inconsistentRows } = checker;
|
|
36
|
-
const fn = (async ({ operation }, context, option) => {
|
|
37
|
-
const { filter: operationFilter, action } = operation;
|
|
38
|
-
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
39
|
-
if (['select', 'count', 'stat'].includes(action)) {
|
|
40
|
-
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [operationFilter, filter2]);
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
if (await (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)) {
|
|
45
|
-
return 0;
|
|
46
|
-
}
|
|
47
|
-
if (inconsistentRows) {
|
|
48
|
-
const { entity: entity2, selection: selection2 } = inconsistentRows;
|
|
49
|
-
const rows2 = await context.select(entity2, selection2(operationFilter), {
|
|
50
|
-
dontCollect: true,
|
|
51
|
-
blockTrigger: true,
|
|
52
|
-
});
|
|
53
|
-
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg);
|
|
54
|
-
e.addData(entity2, rows2);
|
|
55
|
-
throw e;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
const rows2 = await context.select(entity, {
|
|
59
|
-
data: (0, actionDef_1.getFullProjection)(entity, context.getSchema()),
|
|
60
|
-
filter: Object.assign({}, operationFilter, {
|
|
61
|
-
$not: filter2,
|
|
62
|
-
})
|
|
63
|
-
}, {
|
|
64
|
-
dontCollect: true,
|
|
65
|
-
blockTrigger: true,
|
|
66
|
-
});
|
|
67
|
-
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg);
|
|
68
|
-
e.addData(entity, rows2);
|
|
69
|
-
throw e;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
fn,
|
|
75
|
-
when,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
case 'relation': {
|
|
79
|
-
const { relationFilter, errMsg } = checker;
|
|
80
|
-
const fn = (async ({ operation }, context, option) => {
|
|
81
|
-
if (context.isRoot()) {
|
|
82
|
-
return 0;
|
|
83
|
-
}
|
|
84
|
-
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
|
85
|
-
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
|
86
|
-
const result = typeof relationFilter === 'function' ? await relationFilter(operation, context, option) : relationFilter;
|
|
87
|
-
if (result) {
|
|
88
|
-
const { filter, action } = operation;
|
|
89
|
-
if (action === 'create') {
|
|
90
|
-
console.warn(`${entity}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
if (['select', 'count', 'stat'].includes(action)) {
|
|
94
|
-
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [filter, result]);
|
|
95
|
-
return 0;
|
|
96
|
-
}
|
|
97
|
-
(0, assert_1.default)(filter);
|
|
98
|
-
if (await (0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg;
|
|
102
|
-
throw new Exception_1.OakUserUnpermittedException(entity, operation, errMsg2);
|
|
103
|
-
}
|
|
104
|
-
return 0;
|
|
105
|
-
});
|
|
106
|
-
return {
|
|
107
|
-
fn,
|
|
108
|
-
when,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
case 'logical':
|
|
112
|
-
case 'logicalRelation':
|
|
113
|
-
case 'logicalData': {
|
|
114
|
-
const { checker: checkerFn } = checker;
|
|
115
|
-
const fn = (async ({ operation }, context, option) => {
|
|
116
|
-
if (context.isRoot() && type === 'logicalRelation') {
|
|
117
|
-
return 0;
|
|
118
|
-
}
|
|
119
|
-
await checkerFn(operation, context, option);
|
|
120
|
-
return 0;
|
|
121
|
-
});
|
|
122
|
-
return {
|
|
123
|
-
fn,
|
|
124
|
-
when,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
default: {
|
|
128
|
-
(0, assert_1.default)(false);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
|
133
|
-
function translateCheckerInSyncContext(checker) {
|
|
134
|
-
const { entity, type } = checker;
|
|
135
|
-
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
|
136
|
-
switch (type) {
|
|
137
|
-
case 'data': {
|
|
138
|
-
const { checker: checkerFn } = checker;
|
|
139
|
-
const fn = (operation, context) => checkerFn(operation.data, context);
|
|
140
|
-
return {
|
|
141
|
-
fn,
|
|
142
|
-
when,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
case 'row': {
|
|
146
|
-
const { filter, errMsg } = checker;
|
|
147
|
-
const fn = (operation, context, option) => {
|
|
148
|
-
const { filter: operationFilter, action } = operation;
|
|
149
|
-
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
|
150
|
-
(0, assert_1.default)(operationFilter);
|
|
151
|
-
(0, assert_1.default)(!(filter2 instanceof Promise));
|
|
152
|
-
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg || 'row checker condition illegal');
|
|
156
|
-
throw e;
|
|
157
|
-
};
|
|
158
|
-
return {
|
|
159
|
-
fn,
|
|
160
|
-
when,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
case 'relation': {
|
|
164
|
-
const { relationFilter, errMsg } = checker;
|
|
165
|
-
const fn = (operation, context, option) => {
|
|
166
|
-
if (context.isRoot()) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
const result = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
|
170
|
-
(0, assert_1.default)(!(result instanceof Promise));
|
|
171
|
-
if (result) {
|
|
172
|
-
const { filter, action } = operation;
|
|
173
|
-
if (action === 'create') {
|
|
174
|
-
console.warn(`${entity}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
(0, assert_1.default)(filter);
|
|
178
|
-
if ((0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg;
|
|
182
|
-
throw new Exception_1.OakUserUnpermittedException(entity, operation, errMsg2);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
return {
|
|
186
|
-
fn,
|
|
187
|
-
when,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
case 'logical':
|
|
191
|
-
case 'logicalRelation':
|
|
192
|
-
case 'logicalData': {
|
|
193
|
-
const { checker: checkerFn } = checker;
|
|
194
|
-
const fn = (operation, context, option) => {
|
|
195
|
-
if (context.isRoot() && type === 'logicalRelation') {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
checkerFn(operation, context, option);
|
|
199
|
-
};
|
|
200
|
-
return {
|
|
201
|
-
fn,
|
|
202
|
-
when,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
default: {
|
|
206
|
-
(0, assert_1.default)(false);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
exports.translateCheckerInSyncContext = translateCheckerInSyncContext;
|
|
211
|
-
/**
|
|
212
|
-
* 对对象的删除,检查其是否会产生其他行上的空指针,不允许这种情况的出现
|
|
213
|
-
* @param schema
|
|
214
|
-
* @returns
|
|
215
|
-
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
|
216
|
-
*/
|
|
217
|
-
function createRemoveCheckers(schema) {
|
|
218
|
-
const checkers = [];
|
|
219
|
-
// 先建立所有的一对多的关系
|
|
220
|
-
const OneToManyMatrix = {};
|
|
221
|
-
const OneToManyOnEntityMatrix = {};
|
|
222
|
-
const addToMto = (e, f, attr) => {
|
|
223
|
-
if (OneToManyMatrix[f]) {
|
|
224
|
-
OneToManyMatrix[f]?.push([e, attr]);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
OneToManyMatrix[f] = [[e, attr]];
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
const addToMtoEntity = (e, fs) => {
|
|
231
|
-
for (const f of fs) {
|
|
232
|
-
if (!OneToManyOnEntityMatrix[f]) {
|
|
233
|
-
OneToManyOnEntityMatrix[f] = [e];
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
OneToManyOnEntityMatrix[f]?.push(e);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
for (const entity in schema) {
|
|
241
|
-
if (['operEntity', 'modiEntity', 'userEntityGrant'].includes(entity)) {
|
|
242
|
-
continue; // 系统功能性数据,不用处理
|
|
243
|
-
}
|
|
244
|
-
const { attributes } = schema[entity];
|
|
245
|
-
for (const attr in attributes) {
|
|
246
|
-
if (attributes[attr].type === 'ref') {
|
|
247
|
-
addToMto(entity, attributes[attr].ref, attr);
|
|
248
|
-
}
|
|
249
|
-
else if (attr === 'entity') {
|
|
250
|
-
if (attributes[attr].ref) {
|
|
251
|
-
addToMtoEntity(entity, attributes[attr].ref);
|
|
252
|
-
}
|
|
253
|
-
else if (process.env.NODE_ENV === 'development') {
|
|
254
|
-
console.warn(`${entity}的entity反指指针找不到有效的对象`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
// 当删除一时,要确认多上面没有指向一的数据
|
|
260
|
-
const entities = (0, lodash_1.union)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
|
261
|
-
for (const entity of entities) {
|
|
262
|
-
checkers.push({
|
|
263
|
-
entity: entity,
|
|
264
|
-
action: 'remove',
|
|
265
|
-
type: 'logical',
|
|
266
|
-
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
267
|
-
checker: (operation, context, option) => {
|
|
268
|
-
const promises = [];
|
|
269
|
-
if (OneToManyMatrix[entity]) {
|
|
270
|
-
for (const otm of OneToManyMatrix[entity]) {
|
|
271
|
-
const [e, attr] = otm;
|
|
272
|
-
const proj = {
|
|
273
|
-
id: 1,
|
|
274
|
-
[attr]: 1,
|
|
275
|
-
};
|
|
276
|
-
const filter = operation.filter && {
|
|
277
|
-
[attr.slice(0, attr.length - 2)]: operation.filter
|
|
278
|
-
};
|
|
279
|
-
const result = context.select(e, {
|
|
280
|
-
data: proj,
|
|
281
|
-
filter,
|
|
282
|
-
indexFrom: 0,
|
|
283
|
-
count: 1
|
|
284
|
-
}, { dontCollect: true, ignoreAttrMiss: true });
|
|
285
|
-
if (result instanceof Promise) {
|
|
286
|
-
promises.push(result.then(([row]) => {
|
|
287
|
-
if (row) {
|
|
288
|
-
const err = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${e}」关联的行`);
|
|
289
|
-
err.addData(e, [row]);
|
|
290
|
-
throw err;
|
|
291
|
-
}
|
|
292
|
-
}));
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
const [row] = result;
|
|
296
|
-
if (row) {
|
|
297
|
-
const err = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${e}」关联的行`);
|
|
298
|
-
err.addData(e, [row]);
|
|
299
|
-
throw err;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (OneToManyOnEntityMatrix[entity]) {
|
|
305
|
-
for (const otm of OneToManyOnEntityMatrix[entity]) {
|
|
306
|
-
const proj = {
|
|
307
|
-
id: 1,
|
|
308
|
-
entity: 1,
|
|
309
|
-
entityId: 1,
|
|
310
|
-
};
|
|
311
|
-
const filter = operation.filter && {
|
|
312
|
-
[entity]: operation.filter
|
|
313
|
-
};
|
|
314
|
-
const result = context.select(otm, {
|
|
315
|
-
data: proj,
|
|
316
|
-
filter,
|
|
317
|
-
indexFrom: 0,
|
|
318
|
-
count: 1
|
|
319
|
-
}, { dontCollect: true, ignoreAttrMiss: true });
|
|
320
|
-
if (result instanceof Promise) {
|
|
321
|
-
promises.push(result.then(([row]) => {
|
|
322
|
-
if (row) {
|
|
323
|
-
const e = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${otm}」关联的行`);
|
|
324
|
-
e.addData(otm, [row]);
|
|
325
|
-
throw e;
|
|
326
|
-
}
|
|
327
|
-
}));
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
const [row] = result;
|
|
331
|
-
if (row) {
|
|
332
|
-
const record = {
|
|
333
|
-
a: 's',
|
|
334
|
-
d: {
|
|
335
|
-
[otm]: {
|
|
336
|
-
[row.id]: row,
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
const e = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${otm}」关联的行`);
|
|
341
|
-
e.addData(otm, [row]);
|
|
342
|
-
throw e;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (promises.length > 0) {
|
|
348
|
-
return Promise.all(promises).then(() => undefined);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return checkers;
|
|
354
|
-
}
|
|
355
|
-
exports.createRemoveCheckers = createRemoveCheckers;
|
|
356
|
-
function checkAttributeLegal(schema, entity, data) {
|
|
357
|
-
const { attributes } = schema[entity];
|
|
358
|
-
for (const attr in data) {
|
|
359
|
-
if (attributes[attr]) {
|
|
360
|
-
const { type, params, default: defaultValue, enumeration, notNull } = attributes[attr];
|
|
361
|
-
if (data[attr] === null || data[attr] === undefined) {
|
|
362
|
-
if (notNull && defaultValue === undefined) {
|
|
363
|
-
throw new Exception_1.OakAttrNotNullException(entity, [attr]);
|
|
364
|
-
}
|
|
365
|
-
if (defaultValue !== undefined) {
|
|
366
|
-
Object.assign(data, {
|
|
367
|
-
[attr]: defaultValue,
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
switch (type) {
|
|
373
|
-
case 'char':
|
|
374
|
-
case 'varchar': {
|
|
375
|
-
if (typeof data[attr] !== 'string') {
|
|
376
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a string');
|
|
377
|
-
}
|
|
378
|
-
const { length } = params;
|
|
379
|
-
if (length && data[attr].length > length) {
|
|
380
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too long');
|
|
381
|
-
}
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
case 'int':
|
|
385
|
-
case 'smallint':
|
|
386
|
-
case 'tinyint':
|
|
387
|
-
case 'bigint':
|
|
388
|
-
case 'decimal':
|
|
389
|
-
case 'money': {
|
|
390
|
-
if (typeof data[attr] !== 'number') {
|
|
391
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a number');
|
|
392
|
-
}
|
|
393
|
-
const { min, max } = params || {};
|
|
394
|
-
if (typeof min === 'number' && data[attr] < min) {
|
|
395
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too small');
|
|
396
|
-
}
|
|
397
|
-
if (typeof max === 'number' && data[attr] > max) {
|
|
398
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too big');
|
|
399
|
-
}
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
case 'enum': {
|
|
403
|
-
(0, assert_1.default)(enumeration);
|
|
404
|
-
if (!enumeration.includes(data[attr])) {
|
|
405
|
-
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumeration');
|
|
406
|
-
}
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
// 这里似乎还有一种update中带cascade remove的case,等遇到再说(貌似cascadeUpdate没有处理完整这种情况) by Xc
|
|
413
|
-
if (typeof data[attr] === 'object' && data[attr]?.action === 'remove') {
|
|
414
|
-
console.warn('cascade remove可能是未处理的边界,请注意');
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
function createCreateCheckers(schema) {
|
|
420
|
-
const checkers = [];
|
|
421
|
-
for (const entity in schema) {
|
|
422
|
-
const { attributes, actions } = schema[entity];
|
|
423
|
-
const notNullAttrs = Object.keys(attributes).filter(ele => attributes[ele].notNull);
|
|
424
|
-
const updateActions = (0, lodash_1.difference)(actions, action_1.excludeUpdateActions);
|
|
425
|
-
checkers.push({
|
|
426
|
-
entity,
|
|
427
|
-
type: 'data',
|
|
428
|
-
action: 'create',
|
|
429
|
-
checker: (data) => {
|
|
430
|
-
const checkData = (data2) => {
|
|
431
|
-
const illegalNullAttrs = (0, lodash_1.difference)(notNullAttrs, Object.keys(data2).filter(ele => data2[ele] !== null));
|
|
432
|
-
if (illegalNullAttrs.length > 0) {
|
|
433
|
-
const emtpyAttrs = [];
|
|
434
|
-
// 要处理多对一的cascade create
|
|
435
|
-
for (const attr of illegalNullAttrs) {
|
|
436
|
-
if (attr === 'entityId') {
|
|
437
|
-
if (illegalNullAttrs.includes('entity')) {
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
else if (attr === 'entity' && attributes[attr].ref) {
|
|
442
|
-
let hasCascadeCreate = false;
|
|
443
|
-
for (const ref of attributes[attr].ref) {
|
|
444
|
-
if (data2[ref] && data2[ref].action === 'create') {
|
|
445
|
-
hasCascadeCreate = true;
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (hasCascadeCreate) {
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
else if (attributes[attr].type === 'ref') {
|
|
454
|
-
const ref = attributes[attr].ref;
|
|
455
|
-
const attr2 = attr.slice(0, attr.length - 2);
|
|
456
|
-
if (data2[attr2] && data2[attr2].action === 'create') {
|
|
457
|
-
continue;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
// 到这里说明确实是有not null的属性没有赋值
|
|
461
|
-
emtpyAttrs.push(attr);
|
|
462
|
-
}
|
|
463
|
-
if (emtpyAttrs.length > 0) {
|
|
464
|
-
throw new Exception_1.OakAttrNotNullException(entity, emtpyAttrs);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
checkAttributeLegal(schema, entity, data2);
|
|
468
|
-
};
|
|
469
|
-
if (data instanceof Array) {
|
|
470
|
-
data.forEach(ele => checkData(ele));
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
checkData(data);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}, {
|
|
477
|
-
entity,
|
|
478
|
-
type: 'data',
|
|
479
|
-
action: updateActions,
|
|
480
|
-
checker: (data) => {
|
|
481
|
-
checkAttributeLegal(schema, entity, data);
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
return checkers;
|
|
486
|
-
}
|
|
487
|
-
exports.createCreateCheckers = createCreateCheckers;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCreateCheckers = exports.createRemoveCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
+
const filter_1 = require("../store/filter");
|
|
7
|
+
const Exception_1 = require("../types/Exception");
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const actionDef_1 = require("./actionDef");
|
|
10
|
+
const lodash_1 = require("../utils/lodash");
|
|
11
|
+
const action_1 = require("../actions/action");
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param checker 要翻译的checker
|
|
15
|
+
* @param silent 如果silent,则row和relation类型的checker只会把限制条件加到查询上,而不报错(除掉create动作)
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
function translateCheckerInAsyncContext(checker) {
|
|
19
|
+
const { entity, type } = checker;
|
|
20
|
+
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
|
21
|
+
switch (type) {
|
|
22
|
+
case 'data': {
|
|
23
|
+
const { checker: checkerFn } = checker;
|
|
24
|
+
const fn = (async ({ operation }, context) => {
|
|
25
|
+
const { data } = operation;
|
|
26
|
+
await checkerFn(data, context);
|
|
27
|
+
return 0;
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
fn,
|
|
31
|
+
when,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
case 'row': {
|
|
35
|
+
const { filter, errMsg, inconsistentRows } = checker;
|
|
36
|
+
const fn = (async ({ operation }, context, option) => {
|
|
37
|
+
const { filter: operationFilter, action } = operation;
|
|
38
|
+
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
|
39
|
+
if (['select', 'count', 'stat'].includes(action)) {
|
|
40
|
+
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [operationFilter, filter2]);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
if (await (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)) {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
if (inconsistentRows) {
|
|
48
|
+
const { entity: entity2, selection: selection2 } = inconsistentRows;
|
|
49
|
+
const rows2 = await context.select(entity2, selection2(operationFilter), {
|
|
50
|
+
dontCollect: true,
|
|
51
|
+
blockTrigger: true,
|
|
52
|
+
});
|
|
53
|
+
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg);
|
|
54
|
+
e.addData(entity2, rows2);
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const rows2 = await context.select(entity, {
|
|
59
|
+
data: (0, actionDef_1.getFullProjection)(entity, context.getSchema()),
|
|
60
|
+
filter: Object.assign({}, operationFilter, {
|
|
61
|
+
$not: filter2,
|
|
62
|
+
})
|
|
63
|
+
}, {
|
|
64
|
+
dontCollect: true,
|
|
65
|
+
blockTrigger: true,
|
|
66
|
+
});
|
|
67
|
+
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg);
|
|
68
|
+
e.addData(entity, rows2);
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
fn,
|
|
75
|
+
when,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
case 'relation': {
|
|
79
|
+
const { relationFilter, errMsg } = checker;
|
|
80
|
+
const fn = (async ({ operation }, context, option) => {
|
|
81
|
+
if (context.isRoot()) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
|
85
|
+
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
|
86
|
+
const result = typeof relationFilter === 'function' ? await relationFilter(operation, context, option) : relationFilter;
|
|
87
|
+
if (result) {
|
|
88
|
+
const { filter, action } = operation;
|
|
89
|
+
if (action === 'create') {
|
|
90
|
+
console.warn(`${entity}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
if (['select', 'count', 'stat'].includes(action)) {
|
|
94
|
+
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [filter, result]);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
(0, assert_1.default)(filter);
|
|
98
|
+
if (await (0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg;
|
|
102
|
+
throw new Exception_1.OakUserUnpermittedException(entity, operation, errMsg2);
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
fn,
|
|
108
|
+
when,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case 'logical':
|
|
112
|
+
case 'logicalRelation':
|
|
113
|
+
case 'logicalData': {
|
|
114
|
+
const { checker: checkerFn } = checker;
|
|
115
|
+
const fn = (async ({ operation }, context, option) => {
|
|
116
|
+
if (context.isRoot() && type === 'logicalRelation') {
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
await checkerFn(operation, context, option);
|
|
120
|
+
return 0;
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
fn,
|
|
124
|
+
when,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
default: {
|
|
128
|
+
(0, assert_1.default)(false);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
|
133
|
+
function translateCheckerInSyncContext(checker) {
|
|
134
|
+
const { entity, type } = checker;
|
|
135
|
+
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
|
136
|
+
switch (type) {
|
|
137
|
+
case 'data': {
|
|
138
|
+
const { checker: checkerFn } = checker;
|
|
139
|
+
const fn = (operation, context) => checkerFn(operation.data, context);
|
|
140
|
+
return {
|
|
141
|
+
fn,
|
|
142
|
+
when,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
case 'row': {
|
|
146
|
+
const { filter, errMsg } = checker;
|
|
147
|
+
const fn = (operation, context, option) => {
|
|
148
|
+
const { filter: operationFilter, action } = operation;
|
|
149
|
+
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
|
150
|
+
(0, assert_1.default)(operationFilter);
|
|
151
|
+
(0, assert_1.default)(!(filter2 instanceof Promise));
|
|
152
|
+
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg || 'row checker condition illegal');
|
|
156
|
+
throw e;
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
fn,
|
|
160
|
+
when,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
case 'relation': {
|
|
164
|
+
const { relationFilter, errMsg } = checker;
|
|
165
|
+
const fn = (operation, context, option) => {
|
|
166
|
+
if (context.isRoot()) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const result = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
|
170
|
+
(0, assert_1.default)(!(result instanceof Promise));
|
|
171
|
+
if (result) {
|
|
172
|
+
const { filter, action } = operation;
|
|
173
|
+
if (action === 'create') {
|
|
174
|
+
console.warn(`${entity}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
(0, assert_1.default)(filter);
|
|
178
|
+
if ((0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg;
|
|
182
|
+
throw new Exception_1.OakUserUnpermittedException(entity, operation, errMsg2);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
fn,
|
|
187
|
+
when,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
case 'logical':
|
|
191
|
+
case 'logicalRelation':
|
|
192
|
+
case 'logicalData': {
|
|
193
|
+
const { checker: checkerFn } = checker;
|
|
194
|
+
const fn = (operation, context, option) => {
|
|
195
|
+
if (context.isRoot() && type === 'logicalRelation') {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
checkerFn(operation, context, option);
|
|
199
|
+
};
|
|
200
|
+
return {
|
|
201
|
+
fn,
|
|
202
|
+
when,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
default: {
|
|
206
|
+
(0, assert_1.default)(false);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
exports.translateCheckerInSyncContext = translateCheckerInSyncContext;
|
|
211
|
+
/**
|
|
212
|
+
* 对对象的删除,检查其是否会产生其他行上的空指针,不允许这种情况的出现
|
|
213
|
+
* @param schema
|
|
214
|
+
* @returns
|
|
215
|
+
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
|
216
|
+
*/
|
|
217
|
+
function createRemoveCheckers(schema) {
|
|
218
|
+
const checkers = [];
|
|
219
|
+
// 先建立所有的一对多的关系
|
|
220
|
+
const OneToManyMatrix = {};
|
|
221
|
+
const OneToManyOnEntityMatrix = {};
|
|
222
|
+
const addToMto = (e, f, attr) => {
|
|
223
|
+
if (OneToManyMatrix[f]) {
|
|
224
|
+
OneToManyMatrix[f]?.push([e, attr]);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
OneToManyMatrix[f] = [[e, attr]];
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const addToMtoEntity = (e, fs) => {
|
|
231
|
+
for (const f of fs) {
|
|
232
|
+
if (!OneToManyOnEntityMatrix[f]) {
|
|
233
|
+
OneToManyOnEntityMatrix[f] = [e];
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
OneToManyOnEntityMatrix[f]?.push(e);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
for (const entity in schema) {
|
|
241
|
+
if (['operEntity', 'modiEntity', 'userEntityGrant'].includes(entity)) {
|
|
242
|
+
continue; // 系统功能性数据,不用处理
|
|
243
|
+
}
|
|
244
|
+
const { attributes } = schema[entity];
|
|
245
|
+
for (const attr in attributes) {
|
|
246
|
+
if (attributes[attr].type === 'ref') {
|
|
247
|
+
addToMto(entity, attributes[attr].ref, attr);
|
|
248
|
+
}
|
|
249
|
+
else if (attr === 'entity') {
|
|
250
|
+
if (attributes[attr].ref) {
|
|
251
|
+
addToMtoEntity(entity, attributes[attr].ref);
|
|
252
|
+
}
|
|
253
|
+
else if (process.env.NODE_ENV === 'development') {
|
|
254
|
+
console.warn(`${entity}的entity反指指针找不到有效的对象`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// 当删除一时,要确认多上面没有指向一的数据
|
|
260
|
+
const entities = (0, lodash_1.union)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
|
261
|
+
for (const entity of entities) {
|
|
262
|
+
checkers.push({
|
|
263
|
+
entity: entity,
|
|
264
|
+
action: 'remove',
|
|
265
|
+
type: 'logical',
|
|
266
|
+
priority: types_1.CHECKER_MAX_PRIORITY,
|
|
267
|
+
checker: (operation, context, option) => {
|
|
268
|
+
const promises = [];
|
|
269
|
+
if (OneToManyMatrix[entity]) {
|
|
270
|
+
for (const otm of OneToManyMatrix[entity]) {
|
|
271
|
+
const [e, attr] = otm;
|
|
272
|
+
const proj = {
|
|
273
|
+
id: 1,
|
|
274
|
+
[attr]: 1,
|
|
275
|
+
};
|
|
276
|
+
const filter = operation.filter && {
|
|
277
|
+
[attr.slice(0, attr.length - 2)]: operation.filter
|
|
278
|
+
};
|
|
279
|
+
const result = context.select(e, {
|
|
280
|
+
data: proj,
|
|
281
|
+
filter,
|
|
282
|
+
indexFrom: 0,
|
|
283
|
+
count: 1
|
|
284
|
+
}, { dontCollect: true, ignoreAttrMiss: true });
|
|
285
|
+
if (result instanceof Promise) {
|
|
286
|
+
promises.push(result.then(([row]) => {
|
|
287
|
+
if (row) {
|
|
288
|
+
const err = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${e}」关联的行`);
|
|
289
|
+
err.addData(e, [row]);
|
|
290
|
+
throw err;
|
|
291
|
+
}
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const [row] = result;
|
|
296
|
+
if (row) {
|
|
297
|
+
const err = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${e}」关联的行`);
|
|
298
|
+
err.addData(e, [row]);
|
|
299
|
+
throw err;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (OneToManyOnEntityMatrix[entity]) {
|
|
305
|
+
for (const otm of OneToManyOnEntityMatrix[entity]) {
|
|
306
|
+
const proj = {
|
|
307
|
+
id: 1,
|
|
308
|
+
entity: 1,
|
|
309
|
+
entityId: 1,
|
|
310
|
+
};
|
|
311
|
+
const filter = operation.filter && {
|
|
312
|
+
[entity]: operation.filter
|
|
313
|
+
};
|
|
314
|
+
const result = context.select(otm, {
|
|
315
|
+
data: proj,
|
|
316
|
+
filter,
|
|
317
|
+
indexFrom: 0,
|
|
318
|
+
count: 1
|
|
319
|
+
}, { dontCollect: true, ignoreAttrMiss: true });
|
|
320
|
+
if (result instanceof Promise) {
|
|
321
|
+
promises.push(result.then(([row]) => {
|
|
322
|
+
if (row) {
|
|
323
|
+
const e = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${otm}」关联的行`);
|
|
324
|
+
e.addData(otm, [row]);
|
|
325
|
+
throw e;
|
|
326
|
+
}
|
|
327
|
+
}));
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const [row] = result;
|
|
331
|
+
if (row) {
|
|
332
|
+
const record = {
|
|
333
|
+
a: 's',
|
|
334
|
+
d: {
|
|
335
|
+
[otm]: {
|
|
336
|
+
[row.id]: row,
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const e = new Exception_1.OakRowInconsistencyException(undefined, `您无法删除存在有效数据「${otm}」关联的行`);
|
|
341
|
+
e.addData(otm, [row]);
|
|
342
|
+
throw e;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (promises.length > 0) {
|
|
348
|
+
return Promise.all(promises).then(() => undefined);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return checkers;
|
|
354
|
+
}
|
|
355
|
+
exports.createRemoveCheckers = createRemoveCheckers;
|
|
356
|
+
function checkAttributeLegal(schema, entity, data) {
|
|
357
|
+
const { attributes } = schema[entity];
|
|
358
|
+
for (const attr in data) {
|
|
359
|
+
if (attributes[attr]) {
|
|
360
|
+
const { type, params, default: defaultValue, enumeration, notNull } = attributes[attr];
|
|
361
|
+
if (data[attr] === null || data[attr] === undefined) {
|
|
362
|
+
if (notNull && defaultValue === undefined) {
|
|
363
|
+
throw new Exception_1.OakAttrNotNullException(entity, [attr]);
|
|
364
|
+
}
|
|
365
|
+
if (defaultValue !== undefined) {
|
|
366
|
+
Object.assign(data, {
|
|
367
|
+
[attr]: defaultValue,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
switch (type) {
|
|
373
|
+
case 'char':
|
|
374
|
+
case 'varchar': {
|
|
375
|
+
if (typeof data[attr] !== 'string') {
|
|
376
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a string');
|
|
377
|
+
}
|
|
378
|
+
const { length } = params;
|
|
379
|
+
if (length && data[attr].length > length) {
|
|
380
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too long');
|
|
381
|
+
}
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case 'int':
|
|
385
|
+
case 'smallint':
|
|
386
|
+
case 'tinyint':
|
|
387
|
+
case 'bigint':
|
|
388
|
+
case 'decimal':
|
|
389
|
+
case 'money': {
|
|
390
|
+
if (typeof data[attr] !== 'number') {
|
|
391
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a number');
|
|
392
|
+
}
|
|
393
|
+
const { min, max } = params || {};
|
|
394
|
+
if (typeof min === 'number' && data[attr] < min) {
|
|
395
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too small');
|
|
396
|
+
}
|
|
397
|
+
if (typeof max === 'number' && data[attr] > max) {
|
|
398
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too big');
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
case 'enum': {
|
|
403
|
+
(0, assert_1.default)(enumeration);
|
|
404
|
+
if (!enumeration.includes(data[attr])) {
|
|
405
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumeration');
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
// 这里似乎还有一种update中带cascade remove的case,等遇到再说(貌似cascadeUpdate没有处理完整这种情况) by Xc
|
|
413
|
+
if (typeof data[attr] === 'object' && data[attr]?.action === 'remove') {
|
|
414
|
+
console.warn('cascade remove可能是未处理的边界,请注意');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function createCreateCheckers(schema) {
|
|
420
|
+
const checkers = [];
|
|
421
|
+
for (const entity in schema) {
|
|
422
|
+
const { attributes, actions } = schema[entity];
|
|
423
|
+
const notNullAttrs = Object.keys(attributes).filter(ele => attributes[ele].notNull);
|
|
424
|
+
const updateActions = (0, lodash_1.difference)(actions, action_1.excludeUpdateActions);
|
|
425
|
+
checkers.push({
|
|
426
|
+
entity,
|
|
427
|
+
type: 'data',
|
|
428
|
+
action: 'create',
|
|
429
|
+
checker: (data) => {
|
|
430
|
+
const checkData = (data2) => {
|
|
431
|
+
const illegalNullAttrs = (0, lodash_1.difference)(notNullAttrs, Object.keys(data2).filter(ele => data2[ele] !== null));
|
|
432
|
+
if (illegalNullAttrs.length > 0) {
|
|
433
|
+
const emtpyAttrs = [];
|
|
434
|
+
// 要处理多对一的cascade create
|
|
435
|
+
for (const attr of illegalNullAttrs) {
|
|
436
|
+
if (attr === 'entityId') {
|
|
437
|
+
if (illegalNullAttrs.includes('entity')) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
else if (attr === 'entity' && attributes[attr].ref) {
|
|
442
|
+
let hasCascadeCreate = false;
|
|
443
|
+
for (const ref of attributes[attr].ref) {
|
|
444
|
+
if (data2[ref] && data2[ref].action === 'create') {
|
|
445
|
+
hasCascadeCreate = true;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (hasCascadeCreate) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
else if (attributes[attr].type === 'ref') {
|
|
454
|
+
const ref = attributes[attr].ref;
|
|
455
|
+
const attr2 = attr.slice(0, attr.length - 2);
|
|
456
|
+
if (data2[attr2] && data2[attr2].action === 'create') {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// 到这里说明确实是有not null的属性没有赋值
|
|
461
|
+
emtpyAttrs.push(attr);
|
|
462
|
+
}
|
|
463
|
+
if (emtpyAttrs.length > 0) {
|
|
464
|
+
throw new Exception_1.OakAttrNotNullException(entity, emtpyAttrs);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
checkAttributeLegal(schema, entity, data2);
|
|
468
|
+
};
|
|
469
|
+
if (data instanceof Array) {
|
|
470
|
+
data.forEach(ele => checkData(ele));
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
checkData(data);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}, {
|
|
477
|
+
entity,
|
|
478
|
+
type: 'data',
|
|
479
|
+
action: updateActions,
|
|
480
|
+
checker: (data) => {
|
|
481
|
+
checkAttributeLegal(schema, entity, data);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return checkers;
|
|
486
|
+
}
|
|
487
|
+
exports.createCreateCheckers = createCreateCheckers;
|