重构了create和select的checker
This commit is contained in:
parent
da240d298f
commit
149198c526
|
|
@ -2,8 +2,8 @@ import { AuthDefDict, Checker, EntityDict, OperateOption, SelectOption, StorageS
|
|||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from "./AsyncRowStore";
|
||||
import { SyncContext } from './SyncRowStore';
|
||||
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): {
|
||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
||||
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
||||
fn: Trigger<ED, T, Cxt>['fn'];
|
||||
when: 'before' | 'after';
|
||||
};
|
||||
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ var relation_1 = require("./relation");
|
|||
var uuid_1 = require("../utils/uuid");
|
||||
function translateCheckerInAsyncContext(checker) {
|
||||
var _this = this;
|
||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
||||
var entity = checker.entity, type = checker.type;
|
||||
var when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
var checkerFn_1 = checker.checker;
|
||||
|
|
@ -102,44 +102,36 @@ function translateCheckerInAsyncContext(checker) {
|
|||
};
|
||||
}
|
||||
case 'relation': {
|
||||
var relationFilter_1 = checker.relationFilter, errMsg_2 = checker.errMsg;
|
||||
var relationFilter_1 = checker.relationFilter, errMsg = checker.errMsg;
|
||||
var fn = (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var filter2, data, filter, _b, _c, _d;
|
||||
return tslib_1.__generator(this, function (_e) {
|
||||
switch (_e.label) {
|
||||
var result, _b;
|
||||
return tslib_1.__generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
if (context.isRoot()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
if (!(operation.action === 'create')) return [3 /*break*/, 3];
|
||||
if (!(typeof relationFilter_1 === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
||||
case 1:
|
||||
filter2 = _e.sent();
|
||||
data = operation.data;
|
||||
filter = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(function (ele) { return ele.id; }),
|
||||
},
|
||||
} : {
|
||||
id: data.id,
|
||||
};
|
||||
return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, filter, true)];
|
||||
_b = _c.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
if (_e.sent()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_2);
|
||||
_b = relationFilter_1;
|
||||
_c.label = 3;
|
||||
case 3:
|
||||
_b = operation;
|
||||
_c = filter_1.combineFilters;
|
||||
_d = [operation.filter];
|
||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
||||
case 4:
|
||||
_b.filter = _c.apply(void 0, [_d.concat([_e.sent()])]);
|
||||
_e.label = 5;
|
||||
case 5: return [2 /*return*/, 0];
|
||||
result = _b;
|
||||
if (result) {
|
||||
if (operation.action === 'create') {
|
||||
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
|
||||
}
|
||||
else {
|
||||
operation.filter = (0, filter_1.combineFilters)([operation.filter, result]);
|
||||
}
|
||||
}
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -181,8 +173,8 @@ function translateCheckerInAsyncContext(checker) {
|
|||
}
|
||||
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
||||
function translateCheckerInSyncContext(checker) {
|
||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
||||
var entity = checker.entity, type = checker.type;
|
||||
var when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
var checkerFn_3 = checker.checker;
|
||||
|
|
@ -193,7 +185,7 @@ function translateCheckerInSyncContext(checker) {
|
|||
};
|
||||
}
|
||||
case 'row': {
|
||||
var filter_3 = checker.filter, errMsg_3 = checker.errMsg;
|
||||
var filter_3 = checker.filter, errMsg_2 = checker.errMsg;
|
||||
var fn = function (operation, context, option) {
|
||||
var operationFilter = operation.filter, action = operation.action;
|
||||
var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3;
|
||||
|
|
@ -207,7 +199,7 @@ function translateCheckerInSyncContext(checker) {
|
|||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
||||
return;
|
||||
}
|
||||
var e = new Exception_1.OakRowInconsistencyException(undefined, errMsg_3);
|
||||
var e = new Exception_1.OakRowInconsistencyException(undefined, errMsg_2);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
@ -217,28 +209,25 @@ function translateCheckerInSyncContext(checker) {
|
|||
};
|
||||
}
|
||||
case 'relation': {
|
||||
var relationFilter_2 = checker.relationFilter, errMsg_4 = checker.errMsg;
|
||||
var relationFilter_2 = checker.relationFilter, errMsg_3 = checker.errMsg;
|
||||
var fn = function (operation, context, option) {
|
||||
if (context.isRoot()) {
|
||||
return;
|
||||
}
|
||||
var filter2 = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
|
||||
var filter = operation.filter, action = operation.action;
|
||||
var filter3 = filter;
|
||||
if (action === 'create') {
|
||||
var data = operation.data;
|
||||
filter3 = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(function (ele) { return ele.id; }),
|
||||
},
|
||||
} : { id: data.id };
|
||||
var result = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
|
||||
(0, assert_1.default)(!(result instanceof Promise));
|
||||
if (result) {
|
||||
var filter = operation.filter, action = operation.action;
|
||||
if (action === 'create') {
|
||||
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
|
||||
return;
|
||||
}
|
||||
(0, assert_1.default)(filter);
|
||||
if ((0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
||||
return;
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_3);
|
||||
}
|
||||
(0, assert_1.default)(filter3);
|
||||
(0, assert_1.default)(!(filter2 instanceof Promise));
|
||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, filter3, true)) {
|
||||
return;
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_4);
|
||||
};
|
||||
return {
|
||||
fn: fn,
|
||||
|
|
@ -312,11 +301,12 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
|||
};
|
||||
var translateFilterMakerIter = function (entity, iter) {
|
||||
var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]);
|
||||
(0, assert_1.default)(relation === 2 || typeof relation === 'string');
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
var filterMaker_1 = translateRelationFilter(paths[iter]);
|
||||
var filterMaker2_1 = translateRelationFilter(paths[iter]);
|
||||
return function (userId) {
|
||||
var filter = filterMaker_1(userId);
|
||||
var filter = filterMaker2_1(userId);
|
||||
(0, assert_1.default)(filter.id);
|
||||
return {
|
||||
entity: paths[iter],
|
||||
|
|
@ -324,11 +314,10 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
|||
};
|
||||
};
|
||||
}
|
||||
(0, assert_1.default)(typeof relation === 'string');
|
||||
var filterMaker_2 = translateRelationFilter(relation);
|
||||
var filterMaker2_2 = translateRelationFilter(relation);
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var filter = filterMaker_2(userId);
|
||||
var filter = filterMaker2_2(userId);
|
||||
(0, assert_1.default)(filter.id);
|
||||
return _a = {},
|
||||
_a["".concat(paths[iter], "Id")] = filter.id,
|
||||
|
|
@ -336,43 +325,272 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
|||
};
|
||||
}
|
||||
else {
|
||||
var subFilterMaker_1 = translateFilterMakerIter(paths[iter], iter + 1);
|
||||
if (iter === 0) {
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var subFilter = subFilterMaker_1(userId);
|
||||
return _a = {},
|
||||
_a[paths[iter]] = subFilter,
|
||||
_a;
|
||||
};
|
||||
}
|
||||
var filterMaker_1 = relation === 2 ? translateFilterMakerIter(paths[iter], iter + 1) : translateFilterMakerIter(relation, iter + 1);
|
||||
return function (userId) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[paths[iter]] = subFilterMaker_1(userId),
|
||||
_a[paths[iter]] = filterMaker_1(userId),
|
||||
_a);
|
||||
};
|
||||
}
|
||||
};
|
||||
var filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
var filterMaker = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
if (!cascadePath) {
|
||||
return function (oper, userId) { return filterMaker(userId); };
|
||||
}
|
||||
/**
|
||||
* 针对第一层做一下特别优化,比如对象A指向对象B(多对一),如果A的cascadePath是 'B',
|
||||
* 当create A时,会带有Bid。此时生成该B对象上的相关表达式查询返回,可以避免必须将此判定在对象创建之后再做
|
||||
* 另一使用场景是,在查询A时,如果带有Bid(在对象跳一对多子对象场景下很常见),可以提前判定这个查询对某些用户一定返回空集
|
||||
*/
|
||||
var _a = tslib_1.__read(paths, 1), attr = _a[0];
|
||||
var relation = (0, relation_1.judgeRelation)(schema, entity2, attr);
|
||||
(0, assert_1.default)(relation === 2 || typeof relation === 'string');
|
||||
var filterMaker2 = paths.length > 1
|
||||
? (relation === 2 ? translateFilterMakerIter(attr, 1) : translateFilterMakerIter(relation, 1))
|
||||
: (relation === 2 ? translateRelationFilter(attr) : translateRelationFilter(relation));
|
||||
return function (operation, userId) {
|
||||
var action = operation.action;
|
||||
if (action === 'create') {
|
||||
var data = operation.data;
|
||||
var getForeignKeyId_1 = function (d) {
|
||||
if (relation === 2) {
|
||||
if (d.entity === attr && typeof d.entityId === 'string') {
|
||||
return d.entitId;
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException();
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(typeof relation === 'string');
|
||||
if (typeof d["".concat(attr, "Id")] === 'string') {
|
||||
return d["".concat(attr, "Id")];
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException();
|
||||
}
|
||||
};
|
||||
if (relation === 2) {
|
||||
if (data instanceof Array) {
|
||||
var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }));
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||
$count: fkIds.length,
|
||||
};
|
||||
}
|
||||
var fkId_1 = getForeignKeyId_1(data);
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId_1 }),
|
||||
};
|
||||
}
|
||||
(0, assert_1.default)(typeof relation === 'string');
|
||||
if (data instanceof Array) {
|
||||
var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }));
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||
$count: fkIds.length,
|
||||
};
|
||||
}
|
||||
var fkId = getForeignKeyId_1(data);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId }),
|
||||
};
|
||||
}
|
||||
var filter = operation.filter;
|
||||
if (relation === 2 && (filter === null || filter === void 0 ? void 0 : filter.entity) === attr && (filter === null || filter === void 0 ? void 0 : filter.entityId)) {
|
||||
if (typeof filter.entityId === 'string') {
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: filter.entityId }),
|
||||
};
|
||||
}
|
||||
else if (filter.entityId.$in && filter.entityId.$in instanceof Array) {
|
||||
var entityIds = (0, lodash_1.uniq)(filter.entityId.$in);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||
$count: entityIds.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (filter && filter["".concat(attr, "Id")]) {
|
||||
if (typeof filter["".concat(attr, "Id")] === 'string') {
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: filter["".concat(attr, "Id")] }),
|
||||
};
|
||||
}
|
||||
else if (filter["".concat(attr, "Id")].$in && filter["".concat(attr, "Id")].$in instanceof Array) {
|
||||
var entityIds = (0, lodash_1.uniq)(filter["".concat(attr, "Id")].$in);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||
$count: entityIds.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
return filterMaker(userId);
|
||||
};
|
||||
}
|
||||
function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
||||
if (relationItem instanceof Array) {
|
||||
var maker_1 = relationItem.map(function (ele) {
|
||||
var maker = relationItem.map(function (ele) {
|
||||
if (ele instanceof Array) {
|
||||
return ele.map(function (ele2) { return translateCascadeRelationFilterMaker(schema, ele2, entity); });
|
||||
}
|
||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
||||
return translateCascadeRelationFilterMaker(schema, ele, entity);
|
||||
});
|
||||
return function (userId) { return ({
|
||||
$or: maker_1.map(function (ele) { return ({
|
||||
$and: ele.map(function (ele2) { return ele2(userId); })
|
||||
}); })
|
||||
}); };
|
||||
return maker;
|
||||
}
|
||||
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||
return function (userId) { return filterMaker(userId); };
|
||||
return filterMaker;
|
||||
}
|
||||
function makePotentialFilter(operation, context, filterMaker) {
|
||||
var e_1, _a;
|
||||
var userId = context.getCurrentUserId();
|
||||
(0, assert_1.default)(userId);
|
||||
var filters = filterMaker instanceof Array ? filterMaker.map(function (ele) {
|
||||
if (ele instanceof Array) {
|
||||
return ele.map(function (ele2) { return ele2(operation, userId); });
|
||||
}
|
||||
return ele(operation, userId);
|
||||
}) : [filterMaker(operation, userId)];
|
||||
/**
|
||||
* 在下面的逻辑中,如果某个maker返回的是$entity类型,则检查是否有满足条件的项,没有就要抛出异常,有就返回undefined
|
||||
* undefined项即意味着该条件通过
|
||||
* 再加上and和or的布尔逻辑判断,得到最终结果
|
||||
* 还要考虑同步和异步……
|
||||
* 代码比较复杂,因为原先没有$entity这种返回结果的设计
|
||||
* by Xc 20130219
|
||||
*/
|
||||
var filtersOr = [];
|
||||
var isAsyncOr = false;
|
||||
var _loop_1 = function (f) {
|
||||
var e_2, _b;
|
||||
if (f instanceof Array) {
|
||||
var isAsyncAnd = true;
|
||||
var filtersAnd = [];
|
||||
var _loop_2 = function (ff) {
|
||||
if (ff === null || ff === void 0 ? void 0 : ff.$entity) {
|
||||
var _e = ff, $entity = _e.$entity, $filter = _e.$filter, _f = _e.$count, $count_1 = _f === void 0 ? 1 : _f;
|
||||
var count = context.count($entity, {
|
||||
filter: $filter,
|
||||
}, {});
|
||||
if (count instanceof Promise) {
|
||||
isAsyncAnd = true;
|
||||
filtersAnd.push(count.then(function (c2) {
|
||||
if (c2 >= $count_1) {
|
||||
return undefined;
|
||||
}
|
||||
return new Exception_1.OakUserUnpermittedException();
|
||||
}));
|
||||
}
|
||||
else {
|
||||
filtersAnd.push(count >= $count_1 ? undefined : new Exception_1.OakUserUnpermittedException());
|
||||
}
|
||||
}
|
||||
else if (ff) {
|
||||
filtersAnd.push(ff);
|
||||
}
|
||||
};
|
||||
try {
|
||||
for (var f_1 = (e_2 = void 0, tslib_1.__values(f)), f_1_1 = f_1.next(); !f_1_1.done; f_1_1 = f_1.next()) {
|
||||
var ff = f_1_1.value;
|
||||
_loop_2(ff);
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (f_1_1 && !f_1_1.done && (_b = f_1.return)) _b.call(f_1);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
if (isAsyncAnd = true) {
|
||||
isAsyncOr = true;
|
||||
filtersOr.push(isAsyncAnd ? Promise.all(filtersAnd).then(function (fa) {
|
||||
var e_3, _a;
|
||||
var faR = [];
|
||||
try {
|
||||
for (var fa_1 = (e_3 = void 0, tslib_1.__values(fa)), fa_1_1 = fa_1.next(); !fa_1_1.done; fa_1_1 = fa_1.next()) {
|
||||
var faItem = fa_1_1.value;
|
||||
if (faItem instanceof Exception_1.OakUserUnpermittedException) {
|
||||
return faItem;
|
||||
}
|
||||
else if (faItem) {
|
||||
faR.push(faItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (fa_1_1 && !fa_1_1.done && (_a = fa_1.return)) _a.call(fa_1);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
if (faR.length > 0) {
|
||||
return {
|
||||
$and: faR,
|
||||
};
|
||||
}
|
||||
}) : {
|
||||
$and: filtersAnd,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (f === null || f === void 0 ? void 0 : f.$entity) {
|
||||
var _c = f, $entity = _c.$entity, $filter = _c.$filter, _d = _c.$count, $count_2 = _d === void 0 ? 1 : _d;
|
||||
var count = context.count($entity, {
|
||||
filter: $filter,
|
||||
}, {});
|
||||
if (count instanceof Promise) {
|
||||
isAsyncOr = true;
|
||||
filtersOr.push(count.then(function (c2) { return c2 >= $count_2 ? undefined : new Exception_1.OakUserUnpermittedException(); }));
|
||||
}
|
||||
else {
|
||||
filtersOr.push(count >= $count_2 ? undefined : new Exception_1.OakUserUnpermittedException());
|
||||
}
|
||||
}
|
||||
else if (f) {
|
||||
filtersOr.push(f);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
for (var filters_1 = tslib_1.__values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) {
|
||||
var f = filters_1_1.value;
|
||||
_loop_1(f);
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
// or的逻辑是,有一个成功就直接通过
|
||||
var returnOrFilters = function (filters) {
|
||||
if (filters.length === 0 || filters.includes(undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
var foFilters = filters.filter(function (ele) { return ele !== undefined && !(ele instanceof Exception_1.OakUserUnpermittedException); });
|
||||
if (foFilters.length > 0) {
|
||||
return {
|
||||
$or: foFilters,
|
||||
};
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException();
|
||||
};
|
||||
if (isAsyncOr) {
|
||||
return Promise.all(filtersOr)
|
||||
.then(function (filters) { return returnOrFilters(filters); });
|
||||
}
|
||||
return returnOrFilters(filtersOr);
|
||||
}
|
||||
/**
|
||||
* 根据权限定义,创建出相应的checker
|
||||
|
|
@ -382,7 +600,7 @@ function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
|||
*/
|
||||
function createAuthCheckers(schema, authDict) {
|
||||
var checkers = [];
|
||||
var _loop_1 = function (entity) {
|
||||
var _loop_3 = function (entity) {
|
||||
var _a;
|
||||
if (authDict[entity]) {
|
||||
var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
|
||||
|
|
@ -404,14 +622,21 @@ function createAuthCheckers(schema, authDict) {
|
|||
var data = operation.data;
|
||||
(0, assert_1.default)(!(data instanceof Array));
|
||||
var _b = data, relation = _b.relation, _c = entityIdAttr_1, entityId = _b[_c];
|
||||
var userId = context.getCurrentUserId();
|
||||
if (!raFilterMakerDict_1[relation]) {
|
||||
return;
|
||||
}
|
||||
var filter = raFilterMakerDict_1[relation](userId);
|
||||
return _a = {},
|
||||
var filter = makePotentialFilter(operation, context, raFilterMakerDict_1[relation]);
|
||||
if (filter instanceof Promise) {
|
||||
return filter.then(function (f) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[entity] = f,
|
||||
_a);
|
||||
});
|
||||
}
|
||||
return filter ? (_a = {},
|
||||
_a[entity] = filter,
|
||||
_a;
|
||||
_a) : undefined;
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
|
|
@ -429,13 +654,19 @@ function createAuthCheckers(schema, authDict) {
|
|||
(0, assert_1.default)(entityIds.length === 1, "\u5728\u56DE\u6536".concat(userEntityName_1, "\u4E0A\u6743\u9650\u65F6\uFF0C\u5355\u6B21\u56DE\u6536\u6D89\u53CA\u5230\u4E86\u4E0D\u540C\u7684\u5BF9\u8C61\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u88AB\u5141\u8BB8"));
|
||||
// const entityId = entityIds[0]!;
|
||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||
var filtersAnd = relations.map(function (relation) { return raFilterMakerDict_1[relation]; }).filter(function (ele) { return !!ele; }).map(function (ele) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[entity] = makePotentialFilter(operation, context, ele),
|
||||
_a);
|
||||
});
|
||||
if (filtersAnd.find(function (ele) { return ele instanceof Promise; })) {
|
||||
return Promise.all(filtersAnd).then(function (fa) { return ({
|
||||
$and: fa,
|
||||
}); });
|
||||
}
|
||||
return {
|
||||
$and: relations.map(function (relation) { return raFilterMakerDict_1[relation]; }).filter(function (ele) { return !!ele; }).map(function (ele) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[entity] = ele(userId),
|
||||
_a);
|
||||
})
|
||||
$and: filtersAnd
|
||||
};
|
||||
};
|
||||
var toBeRemoved = context.select(userEntityName_1, {
|
||||
|
|
@ -458,7 +689,7 @@ function createAuthCheckers(schema, authDict) {
|
|||
// todo 等实现的时候再写
|
||||
}
|
||||
if (actionAuth) {
|
||||
var _loop_2 = function (a) {
|
||||
var _loop_4 = function (a) {
|
||||
var filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a], entity);
|
||||
checkers.push({
|
||||
entity: entity,
|
||||
|
|
@ -466,20 +697,20 @@ function createAuthCheckers(schema, authDict) {
|
|||
type: 'relation',
|
||||
relationFilter: function (operation, context) {
|
||||
// const { filter } = operation;
|
||||
var filter = filterMaker(context.getCurrentUserId());
|
||||
var filter = makePotentialFilter(operation, context, filterMaker);
|
||||
return filter;
|
||||
},
|
||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||
});
|
||||
};
|
||||
for (var a in actionAuth) {
|
||||
_loop_2(a);
|
||||
_loop_4(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (var entity in schema) {
|
||||
_loop_1(entity);
|
||||
_loop_3(entity);
|
||||
}
|
||||
return checkers;
|
||||
}
|
||||
|
|
@ -491,7 +722,7 @@ exports.createAuthCheckers = createAuthCheckers;
|
|||
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||
*/
|
||||
function createRemoveCheckers(schema, authDict) {
|
||||
var e_1, _a;
|
||||
var e_4, _a;
|
||||
var checkers = [];
|
||||
// 先建立所有的一对多的关系
|
||||
var OneToManyMatrix = {};
|
||||
|
|
@ -506,7 +737,7 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
};
|
||||
var addToMtoEntity = function (e, fs) {
|
||||
var e_2, _a;
|
||||
var e_5, _a;
|
||||
var _b;
|
||||
try {
|
||||
for (var fs_1 = tslib_1.__values(fs), fs_1_1 = fs_1.next(); !fs_1_1.done; fs_1_1 = fs_1.next()) {
|
||||
|
|
@ -519,12 +750,12 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (fs_1_1 && !fs_1_1.done && (_a = fs_1.return)) _a.call(fs_1);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
}
|
||||
};
|
||||
for (var entity in schema) {
|
||||
|
|
@ -548,16 +779,16 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
// 当删除一时,要确认多上面没有指向一的数据
|
||||
var entities = (0, lodash_1.union)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
||||
var _loop_3 = function (entity) {
|
||||
var _loop_5 = function (entity) {
|
||||
checkers.push({
|
||||
entity: entity,
|
||||
action: 'remove',
|
||||
type: 'logical',
|
||||
checker: function (operation, context, option) {
|
||||
var e_3, _a, e_4, _b;
|
||||
var e_6, _a, e_7, _b;
|
||||
var promises = [];
|
||||
if (OneToManyMatrix[entity]) {
|
||||
var _loop_5 = function (otm) {
|
||||
var _loop_7 = function (otm) {
|
||||
var _g, _h;
|
||||
var _j = tslib_1.__read(otm, 2), e = _j[0], attr = _j[1];
|
||||
var proj = (_g = {
|
||||
|
|
@ -594,21 +825,21 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
};
|
||||
try {
|
||||
for (var _c = (e_3 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||
for (var _c = (e_6 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||
var otm = _d.value;
|
||||
_loop_5(otm);
|
||||
_loop_7(otm);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
finally { if (e_6) throw e_6.error; }
|
||||
}
|
||||
}
|
||||
if (OneToManyOnEntityMatrix[entity]) {
|
||||
var _loop_6 = function (otm) {
|
||||
var _loop_8 = function (otm) {
|
||||
var _l, _m, _o;
|
||||
var proj = {
|
||||
id: 1,
|
||||
|
|
@ -652,17 +883,17 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
};
|
||||
try {
|
||||
for (var _e = (e_4 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
|
||||
for (var _e = (e_7 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
|
||||
var otm = _f.value;
|
||||
_loop_6(otm);
|
||||
_loop_8(otm);
|
||||
}
|
||||
}
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
||||
}
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
finally { if (e_7) throw e_7.error; }
|
||||
}
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
|
|
@ -674,23 +905,23 @@ function createRemoveCheckers(schema, authDict) {
|
|||
try {
|
||||
for (var entities_1 = tslib_1.__values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
|
||||
var entity = entities_1_1.value;
|
||||
_loop_3(entity);
|
||||
_loop_5(entity);
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
}
|
||||
var _loop_4 = function (entity) {
|
||||
var e_5, _b;
|
||||
var _loop_6 = function (entity) {
|
||||
var e_8, _b;
|
||||
var cascadeRemove = authDict[entity].cascadeRemove;
|
||||
if (cascadeRemove) {
|
||||
var entitiesOnEntityAttr = [];
|
||||
var hasAllEntity = false;
|
||||
var _loop_7 = function (attr) {
|
||||
var _loop_9 = function (attr) {
|
||||
if (attr === '@entity') {
|
||||
hasAllEntity = true;
|
||||
return "continue";
|
||||
|
|
@ -765,13 +996,13 @@ function createRemoveCheckers(schema, authDict) {
|
|||
}
|
||||
};
|
||||
for (var attr in cascadeRemove) {
|
||||
_loop_7(attr);
|
||||
_loop_9(attr);
|
||||
}
|
||||
if (hasAllEntity) {
|
||||
var attributes = schema[entity].attributes;
|
||||
var ref = attributes.entity.ref;
|
||||
var restEntities = (0, lodash_1.difference)(ref, entitiesOnEntityAttr);
|
||||
var _loop_8 = function (e) {
|
||||
var _loop_10 = function (e) {
|
||||
checkers.push({
|
||||
entity: e,
|
||||
action: 'remove',
|
||||
|
|
@ -805,24 +1036,24 @@ function createRemoveCheckers(schema, authDict) {
|
|||
});
|
||||
};
|
||||
try {
|
||||
for (var restEntities_1 = (e_5 = void 0, tslib_1.__values(restEntities)), restEntities_1_1 = restEntities_1.next(); !restEntities_1_1.done; restEntities_1_1 = restEntities_1.next()) {
|
||||
for (var restEntities_1 = (e_8 = void 0, tslib_1.__values(restEntities)), restEntities_1_1 = restEntities_1.next(); !restEntities_1_1.done; restEntities_1_1 = restEntities_1.next()) {
|
||||
var e = restEntities_1_1.value;
|
||||
_loop_8(e);
|
||||
_loop_10(e);
|
||||
}
|
||||
}
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (restEntities_1_1 && !restEntities_1_1.done && (_b = restEntities_1.return)) _b.call(restEntities_1);
|
||||
}
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
finally { if (e_8) throw e_8.error; }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// 注入声明的cascade删除时的外键处理动作
|
||||
for (var entity in authDict) {
|
||||
_loop_4(entity);
|
||||
_loop_6(entity);
|
||||
}
|
||||
return checkers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove } from ".";
|
||||
import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||
|
|
@ -12,21 +12,21 @@ export declare type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt e
|
|||
type: 'data';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => any | Promise<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => SyncOrAsync<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Selection']['filter']>);
|
||||
};
|
||||
export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'row';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>);
|
||||
errMsg?: string;
|
||||
inconsistentRows?: {
|
||||
entity: keyof ED;
|
||||
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
||||
};
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||
};
|
||||
export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
|
|
@ -34,9 +34,9 @@ export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, C
|
|||
entity: T;
|
||||
when?: 'after';
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>;
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>;
|
||||
errMsg: string;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Selection']['filter']>);
|
||||
};
|
||||
export declare type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
|
|
@ -44,8 +44,8 @@ export declare type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cx
|
|||
when?: 'after';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => any | Promise<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||
};
|
||||
export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
|
|
@ -54,7 +54,7 @@ export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyo
|
|||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => any | Promise<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||
};
|
||||
export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt> | LogicalRelationChecker<ED, T, Cxt>;
|
||||
export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
||||
|
|
|
|||
|
|
@ -20,4 +20,5 @@ declare type IsOptional<T, K extends keyof T> = {
|
|||
export declare type OptionalKeys<T> = {
|
||||
[K in keyof T]: IsOptional<T, K>;
|
||||
}[keyof T];
|
||||
export declare type SyncOrAsync<T> = T | Promise<T>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { addFilterSegment, checkFilterContains, combineFilters } from "../store/
|
|||
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
|
||||
import {
|
||||
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
|
||||
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY
|
||||
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY, RefOrExpression, SyncOrAsync
|
||||
} from "../types";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from "./AsyncRowStore";
|
||||
|
|
@ -16,13 +16,14 @@ import { generateNewId } from '../utils/uuid';
|
|||
|
||||
export function translateCheckerInAsyncContext<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
T extends keyof ED,
|
||||
Cxt extends AsyncContext<ED>
|
||||
>(checker: Checker<ED, keyof ED, Cxt>): {
|
||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
||||
>(checker: Checker<ED, T, Cxt>): {
|
||||
fn: Trigger<ED, T, Cxt>['fn'];
|
||||
when: 'before' | 'after';
|
||||
} {
|
||||
const { entity, type, action } = checker;
|
||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
||||
const { entity, type } = checker;
|
||||
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
const { checker: checkerFn } = checker;
|
||||
|
|
@ -56,7 +57,7 @@ export function translateCheckerInAsyncContext<
|
|||
blockTrigger: true,
|
||||
});
|
||||
|
||||
const e = new OakRowInconsistencyException<ED>(undefined, errMsg);
|
||||
const e = new OakRowInconsistencyException<ED>(undefined, errMsg);
|
||||
e.addData(entity2, rows2);
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -73,10 +74,10 @@ export function translateCheckerInAsyncContext<
|
|||
|
||||
const e = new OakRowInconsistencyException<ED>(undefined, errMsg);
|
||||
e.addData(entity, rows2);
|
||||
throw e;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
|
|
@ -90,29 +91,19 @@ export function translateCheckerInAsyncContext<
|
|||
}
|
||||
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
||||
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
||||
if (operation.action === 'create') {
|
||||
const filter2 = await relationFilter(operation, context, option);
|
||||
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
const filter = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(
|
||||
ele => ele.id,
|
||||
),
|
||||
},
|
||||
} : {
|
||||
id: data.id,
|
||||
};
|
||||
if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, filter, true)) {
|
||||
return 0;
|
||||
const result = typeof relationFilter === 'function' ? await relationFilter(operation, context, option) : relationFilter;
|
||||
|
||||
if (result) {
|
||||
if (operation.action === 'create') {
|
||||
console.warn(`${entity as string}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
||||
}
|
||||
else {
|
||||
operation.filter = combineFilters([operation.filter, result as ED[T]['Selection']['filter']]);
|
||||
}
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
}
|
||||
else {
|
||||
operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
|
||||
}
|
||||
return 0;
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
|
|
@ -127,7 +118,7 @@ export function translateCheckerInAsyncContext<
|
|||
}
|
||||
await checkerFn(operation, context, option);
|
||||
return 0;
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
|
|
@ -147,8 +138,8 @@ export function translateCheckerInSyncContext<
|
|||
fn: (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
||||
when: 'before' | 'after';
|
||||
} {
|
||||
const { entity, type, action } = checker;
|
||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
||||
const { entity, type } = checker;
|
||||
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
const { checker: checkerFn } = checker;
|
||||
|
|
@ -174,7 +165,7 @@ export function translateCheckerInSyncContext<
|
|||
return;
|
||||
}
|
||||
const e = new OakRowInconsistencyException(undefined, errMsg);
|
||||
throw e;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
return {
|
||||
|
|
@ -188,23 +179,21 @@ export function translateCheckerInSyncContext<
|
|||
if (context.isRoot()) {
|
||||
return;
|
||||
}
|
||||
const filter2 = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
||||
const { filter, action } = operation;
|
||||
let filter3 = filter;
|
||||
if (action === 'create') {
|
||||
const { data } = operation as ED[T]['Create'];
|
||||
filter3 = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(ele => ele.id),
|
||||
},
|
||||
} : { id: data.id };
|
||||
const result = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
||||
|
||||
assert(!(result instanceof Promise));
|
||||
if (result) {
|
||||
const { filter, action } = operation;
|
||||
if (action === 'create') {
|
||||
console.warn(`${entity as string}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
||||
return;
|
||||
}
|
||||
assert(filter);
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, result as ED[T]['Selection']['filter'], filter, true)) {
|
||||
return;
|
||||
}
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
}
|
||||
assert(filter3);
|
||||
assert(!(filter2 instanceof Promise));
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, filter3, true)) {
|
||||
return;
|
||||
}
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
};
|
||||
return {
|
||||
fn,
|
||||
|
|
@ -231,10 +220,16 @@ export function translateCheckerInSyncContext<
|
|||
}
|
||||
}
|
||||
|
||||
type FilterMakeFn<ED extends EntityDict & BaseEntityDict> = (operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'], userId: string) => ED[keyof ED]['Selection']['filter'] | {
|
||||
$entity: keyof ED;
|
||||
$filter?: ED[keyof ED]['Selection']['filter'];
|
||||
$count?: number;
|
||||
};
|
||||
|
||||
function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
lch: CascadeRelationItem,
|
||||
entity2: keyof ED): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
||||
entity2: keyof ED): FilterMakeFn<ED> {
|
||||
const { cascadePath, relations } = lch;
|
||||
const paths = cascadePath.split('.');
|
||||
|
||||
|
|
@ -282,11 +277,13 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
|||
|
||||
const translateFilterMakerIter = <T extends keyof ED>(entity: T, iter: number): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||
const relation = judgeRelation(schema, entity, paths[iter]);
|
||||
assert(relation === 2 || typeof relation === 'string');
|
||||
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
const filterMaker = translateRelationFilter(paths[iter]);
|
||||
const filterMaker2 = translateRelationFilter(paths[iter]);
|
||||
return (userId) => {
|
||||
const filter = filterMaker(userId)!;
|
||||
const filter = filterMaker2(userId)!;
|
||||
assert(filter.id);
|
||||
return {
|
||||
entity: paths[iter],
|
||||
|
|
@ -294,10 +291,9 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
|||
};
|
||||
}
|
||||
}
|
||||
assert(typeof relation === 'string');
|
||||
const filterMaker = translateRelationFilter(relation);
|
||||
const filterMaker2 = translateRelationFilter(relation);
|
||||
return (userId) => {
|
||||
const filter = filterMaker(userId)!;
|
||||
const filter = filterMaker2(userId)!;
|
||||
|
||||
assert(filter.id);
|
||||
return {
|
||||
|
|
@ -306,30 +302,119 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
|||
}
|
||||
}
|
||||
else {
|
||||
const subFilterMaker = translateFilterMakerIter(paths[iter], iter + 1);
|
||||
if (iter === 0) {
|
||||
return (userId) => {
|
||||
const subFilter = subFilterMaker(userId);
|
||||
return {
|
||||
[paths[iter]]: subFilter,
|
||||
};
|
||||
};
|
||||
}
|
||||
const filterMaker = relation === 2 ? translateFilterMakerIter(paths[iter], iter + 1) : translateFilterMakerIter(relation, iter + 1);
|
||||
return (userId) => ({
|
||||
[paths[iter]]: subFilterMaker(userId),
|
||||
[paths[iter]]: filterMaker(userId),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
const filterMaker = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
if (!cascadePath) {
|
||||
return (oper, userId) => filterMaker(userId);
|
||||
}
|
||||
/**
|
||||
* 针对第一层做一下特别优化,比如对象A指向对象B(多对一),如果A的cascadePath是 'B',
|
||||
* 当create A时,会带有Bid。此时生成该B对象上的相关表达式查询返回,可以避免必须将此判定在对象创建之后再做
|
||||
* 另一使用场景是,在查询A时,如果带有Bid(在对象跳一对多子对象场景下很常见),可以提前判定这个查询对某些用户一定返回空集
|
||||
*/
|
||||
const [attr] = paths;
|
||||
const relation = judgeRelation(schema, entity2, attr);
|
||||
assert(relation === 2 || typeof relation === 'string');
|
||||
const filterMaker2 = paths.length > 1
|
||||
? (relation === 2 ? translateFilterMakerIter(attr, 1) : translateFilterMakerIter(relation, 1))
|
||||
: (relation === 2 ? translateRelationFilter(attr) : translateRelationFilter(relation));
|
||||
return (operation, userId) => {
|
||||
const { action } = operation as ED[keyof ED]['Operation'];
|
||||
if (action === 'create') {
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
const getForeignKeyId = (d: ED[keyof ED]['CreateSingle']['data']) => {
|
||||
if (relation === 2) {
|
||||
if (d.entity === attr && typeof d.entityId === 'string') {
|
||||
return d.entitId as string;
|
||||
}
|
||||
throw new OakUserUnpermittedException();
|
||||
}
|
||||
else {
|
||||
assert(typeof relation === 'string');
|
||||
if (typeof d[`${attr}Id`] === 'string') {
|
||||
return d[`${attr}Id`] as string;
|
||||
}
|
||||
throw new OakUserUnpermittedException();
|
||||
}
|
||||
};
|
||||
if (relation === 2) {
|
||||
if (data instanceof Array) {
|
||||
const fkIds = uniq(data.map(d => getForeignKeyId(d)));
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||
$count: fkIds.length,
|
||||
};
|
||||
}
|
||||
const fkId = getForeignKeyId(data);
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: fkId }),
|
||||
};
|
||||
}
|
||||
assert(typeof relation === 'string');
|
||||
if (data instanceof Array) {
|
||||
const fkIds = uniq(data.map(d => getForeignKeyId(d)));
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||
$count: fkIds.length,
|
||||
};
|
||||
}
|
||||
const fkId = getForeignKeyId(data);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: fkId }),
|
||||
};
|
||||
}
|
||||
const { filter } = operation;
|
||||
if (relation === 2 && filter?.entity === attr && filter?.entityId) {
|
||||
if (typeof filter.entityId === 'string') {
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: filter.entityId }),
|
||||
};
|
||||
}
|
||||
else if (filter.entityId.$in && filter.entityId.$in instanceof Array) {
|
||||
const entityIds = uniq(filter.entityId.$in);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||
$count: entityIds.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (filter && filter[`${attr}Id`]) {
|
||||
if (typeof filter[`${attr}Id`] === 'string') {
|
||||
return {
|
||||
$entity: attr,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: filter[`${attr}Id`] }),
|
||||
};
|
||||
}
|
||||
else if (filter[`${attr}Id`].$in && filter[`${attr}Id`].$in instanceof Array) {
|
||||
const entityIds = uniq(filter[`${attr}Id`].$in);
|
||||
return {
|
||||
$entity: relation,
|
||||
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||
$count: entityIds.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
return filterMaker(userId);
|
||||
};
|
||||
}
|
||||
|
||||
function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
||||
entity: keyof ED
|
||||
): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
||||
): FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[] {
|
||||
if (relationItem instanceof Array) {
|
||||
const maker = relationItem.map(
|
||||
ele => {
|
||||
|
|
@ -338,21 +423,143 @@ function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
|||
ele2 => translateCascadeRelationFilterMaker(schema, ele2, entity)
|
||||
);
|
||||
}
|
||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
||||
return translateCascadeRelationFilterMaker(schema, ele, entity);
|
||||
}
|
||||
);
|
||||
return (userId) => ({
|
||||
$or: maker.map(
|
||||
ele => ({
|
||||
$and: ele.map(
|
||||
ele2 => ele2(userId)!
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
return maker;
|
||||
}
|
||||
const filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||
return (userId) => filterMaker(userId);
|
||||
return filterMaker;
|
||||
}
|
||||
|
||||
function makePotentialFilter<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>> (
|
||||
operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'],
|
||||
context: Cxt,
|
||||
filterMaker: FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[]): SyncOrAsync<ED[keyof ED]['Selection']['filter']> {
|
||||
const userId = context.getCurrentUserId();
|
||||
assert(userId!);
|
||||
const filters = filterMaker instanceof Array ? filterMaker.map(
|
||||
ele => {
|
||||
if (ele instanceof Array) {
|
||||
return ele.map(
|
||||
ele2 => ele2(operation, userId),
|
||||
);
|
||||
}
|
||||
return ele(operation, userId);
|
||||
}
|
||||
) : [filterMaker(operation, userId)];
|
||||
|
||||
/**
|
||||
* 在下面的逻辑中,如果某个maker返回的是$entity类型,则检查是否有满足条件的项,没有就要抛出异常,有就返回undefined
|
||||
* undefined项即意味着该条件通过
|
||||
* 再加上and和or的布尔逻辑判断,得到最终结果
|
||||
* 还要考虑同步和异步……
|
||||
* 代码比较复杂,因为原先没有$entity这种返回结果的设计
|
||||
* by Xc 20130219
|
||||
*/
|
||||
const filtersOr: (SyncOrAsync<ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>>)[] = [];
|
||||
let isAsyncOr = false;
|
||||
for (const f of filters) {
|
||||
if (f instanceof Array) {
|
||||
let isAsyncAnd = true;
|
||||
const filtersAnd: (SyncOrAsync<ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>>)[] = [];
|
||||
for (const ff of f) {
|
||||
if (ff?.$entity) {
|
||||
const { $entity, $filter, $count = 1 } = ff!;
|
||||
const count = context.count($entity, {
|
||||
filter: $filter,
|
||||
}, {});
|
||||
if (count instanceof Promise) {
|
||||
isAsyncAnd = true;
|
||||
filtersAnd.push(
|
||||
count.then(
|
||||
(c2) => {
|
||||
if (c2 >= $count) {
|
||||
return undefined;
|
||||
}
|
||||
return new OakUserUnpermittedException();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
filtersAnd.push(count >= $count ? undefined : new OakUserUnpermittedException());
|
||||
}
|
||||
}
|
||||
else if (ff) {
|
||||
filtersAnd.push(ff as ED[keyof ED]['Selection']['filter']);
|
||||
}
|
||||
}
|
||||
if (isAsyncAnd = true) {
|
||||
isAsyncOr = true;
|
||||
filtersOr.push(isAsyncAnd ? Promise.all(filtersAnd).then(
|
||||
(fa) => {
|
||||
const faR: ED[keyof ED]['Selection']['filter'][] = [];
|
||||
for (const faItem of fa) {
|
||||
if (faItem instanceof OakUserUnpermittedException) {
|
||||
return faItem;
|
||||
}
|
||||
else if (faItem) {
|
||||
faR.push(faItem);
|
||||
}
|
||||
}
|
||||
if (faR.length > 0) {
|
||||
return {
|
||||
$and: faR,
|
||||
};
|
||||
}
|
||||
}
|
||||
) : ({
|
||||
$and: filtersAnd,
|
||||
} as ED[keyof ED]['Selection']['filter']));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (f?.$entity) {
|
||||
const { $entity, $filter, $count = 1 } = f!;
|
||||
const count = context.count($entity, {
|
||||
filter: $filter,
|
||||
}, {});
|
||||
if (count instanceof Promise) {
|
||||
isAsyncOr = true;
|
||||
filtersOr.push(
|
||||
count.then(
|
||||
(c2) => c2 >= $count ? undefined : new OakUserUnpermittedException()
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
filtersOr.push(count >= $count ? undefined : new OakUserUnpermittedException());
|
||||
}
|
||||
}
|
||||
else if (f) {
|
||||
filtersOr.push(f as ED[keyof ED]['Selection']['filter']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// or的逻辑是,有一个成功就直接通过
|
||||
const returnOrFilters = (filters: (ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>)[]) => {
|
||||
if (filters.length === 0 || filters.includes(undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
const foFilters = filters.filter(
|
||||
ele => ele !== undefined && !(ele instanceof OakUserUnpermittedException)
|
||||
);
|
||||
if (foFilters.length > 0) {
|
||||
return {
|
||||
$or: foFilters,
|
||||
};
|
||||
}
|
||||
throw new OakUserUnpermittedException();
|
||||
};
|
||||
if (isAsyncOr) {
|
||||
return Promise.all(filtersOr)
|
||||
.then(
|
||||
(filters) => returnOrFilters(filters)
|
||||
);
|
||||
}
|
||||
return returnOrFilters(filtersOr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -370,7 +577,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
if (authDict[entity]) {
|
||||
const { relationAuth, actionAuth } = authDict[entity]!;
|
||||
if (relationAuth) {
|
||||
const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
|
||||
const raFilterMakerDict = {} as Record<string, FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[]>;
|
||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||
for (const r in relationAuth) {
|
||||
Object.assign(raFilterMakerDict, {
|
||||
|
|
@ -386,14 +593,20 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
assert(!(data instanceof Array));
|
||||
const { relation, [entityIdAttr]: entityId } = data;
|
||||
const userId = context.getCurrentUserId();
|
||||
if (!raFilterMakerDict[relation]) {
|
||||
return;
|
||||
}
|
||||
const filter = raFilterMakerDict[relation]!(userId!);
|
||||
return {
|
||||
const filter = makePotentialFilter(operation, context, raFilterMakerDict[relation]);
|
||||
if (filter instanceof Promise) {
|
||||
return filter.then(
|
||||
(f) => ({
|
||||
[entity]: f,
|
||||
})
|
||||
);
|
||||
}
|
||||
return filter ? {
|
||||
[entity]: filter,
|
||||
};
|
||||
} : undefined;
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
|
|
@ -405,23 +618,31 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
relationFilter: (operation: any, context: Cxt) => {
|
||||
const userId = context.getCurrentUserId();
|
||||
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]) => {
|
||||
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]): SyncOrAsync<ED[keyof ED]['Selection']['filter']> => {
|
||||
const relations = uniq(rows.map(ele => ele.relation));
|
||||
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
|
||||
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
|
||||
// const entityId = entityIds[0]!;
|
||||
|
||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||
const filtersAnd = relations.map(
|
||||
(relation) => raFilterMakerDict[relation!]
|
||||
).filter(
|
||||
ele => !!ele
|
||||
).map(
|
||||
ele => ({
|
||||
[entity]: makePotentialFilter(operation, context, ele),
|
||||
})
|
||||
);
|
||||
if (filtersAnd.find(ele => ele instanceof Promise)) {
|
||||
return Promise.all(filtersAnd).then(
|
||||
(fa) => ({
|
||||
$and: fa,
|
||||
} as ED[keyof ED]['Selection']['filter'])
|
||||
);
|
||||
}
|
||||
return {
|
||||
$and: relations.map(
|
||||
(relation) => raFilterMakerDict[relation!]
|
||||
).filter(
|
||||
ele => !!ele
|
||||
).map(
|
||||
ele => ({
|
||||
[entity]: ele(userId!),
|
||||
})
|
||||
)
|
||||
$and: filtersAnd
|
||||
} as ED[keyof ED]['Selection']['filter'];
|
||||
};
|
||||
|
||||
|
|
@ -455,7 +676,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
type: 'relation',
|
||||
relationFilter: (operation, context) => {
|
||||
// const { filter } = operation;
|
||||
const filter = filterMaker(context.getCurrentUserId()!);
|
||||
const filter = makePotentialFilter(operation, context, filterMaker);
|
||||
return filter;
|
||||
},
|
||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||
|
|
@ -538,7 +759,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
[attr]: 1,
|
||||
};
|
||||
const filter = operation.filter && {
|
||||
[attr.slice(0, attr.length -2)]: operation.filter
|
||||
[attr.slice(0, attr.length - 2)]: operation.filter
|
||||
}
|
||||
const result = context.select(e, {
|
||||
data: proj,
|
||||
|
|
@ -590,7 +811,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
result.then(
|
||||
([row]) => {
|
||||
if (row) {
|
||||
const e = new OakRowInconsistencyException<ED>(undefined, `您无法删除存在有效数据「${otm as string}」关联的行`);
|
||||
const e = new OakRowInconsistencyException<ED>(undefined, `您无法删除存在有效数据「${otm as string}」关联的行`);
|
||||
e.addData(otm, [row]);
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -614,7 +835,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return Promise.all(promises).then(
|
||||
|
|
@ -653,7 +874,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
data: {},
|
||||
filter: filter ? {
|
||||
[attr]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
return context.operate(entity, {
|
||||
|
|
@ -665,7 +886,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
},
|
||||
filter: filter ? {
|
||||
[attr]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
|
||||
}
|
||||
|
|
@ -687,7 +908,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
data: {},
|
||||
filter: filter ? {
|
||||
[attr]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
return context.operate(entity, {
|
||||
|
|
@ -698,7 +919,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
},
|
||||
filter: filter ? {
|
||||
[attr]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
});
|
||||
|
|
@ -724,7 +945,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
data: {},
|
||||
filter: filter ? {
|
||||
[e]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
return context.operate(entity, {
|
||||
|
|
@ -736,7 +957,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
|||
},
|
||||
filter: filter ? {
|
||||
[e]: filter,
|
||||
}: undefined,
|
||||
} : undefined,
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth, ActionOnRemove } from ".";
|
||||
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||
|
|
@ -15,9 +15,9 @@ export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends A
|
|||
type: 'data';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => any | Promise<any>;
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => SyncOrAsync<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
|||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||
filter: ED[T]['Selection']['filter'] | (
|
||||
(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||
(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||
); // 对行的额外检查条件
|
||||
errMsg?: string;
|
||||
inconsistentRows?: { // 因为这里的限制不一定在本row上,如果不传这个exception,则默认返回本row上的exception
|
||||
|
|
@ -35,7 +35,7 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
|||
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
||||
};
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -45,10 +45,10 @@ export type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt exten
|
|||
entity: T;
|
||||
when?: 'after';
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>; // 生成一个额外的relation相关的filter,加在原先的filter上
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>, // 生成一个额外的relation相关的filter,加在原先的filter上
|
||||
errMsg: string;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -62,8 +62,8 @@ export type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extend
|
|||
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
||||
context: Cxt,
|
||||
option: OperateOption | SelectOption
|
||||
) => any | Promise<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
) => SyncOrAsync<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||
};
|
||||
|
||||
export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
|
|
@ -77,7 +77,7 @@ export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cx
|
|||
context: Cxt,
|
||||
option: OperateOption | SelectOption
|
||||
) => any | Promise<any>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,4 +17,7 @@ export type OneOf<Obj> = ValueOf<OneOfByKey<Obj>>;
|
|||
|
||||
// 判断对象中的optional key
|
||||
type IsOptional<T, K extends keyof T> = { [K1 in Exclude<keyof T, K>]: T[K1] } & { K?: T[K] } extends T ? K : never
|
||||
export type OptionalKeys<T> = { [K in keyof T]: IsOptional<T, K> }[keyof T]
|
||||
export type OptionalKeys<T> = { [K in keyof T]: IsOptional<T, K> }[keyof T]
|
||||
|
||||
// 同或异步返回
|
||||
export type SyncOrAsync<T> = T | Promise<T>;
|
||||
Loading…
Reference in New Issue