Merge branch 'release'

This commit is contained in:
Xu Chang 2023-02-05 22:18:42 +08:00
commit b3de5cb62c
15 changed files with 518 additions and 214 deletions

View File

@ -1,5 +1,5 @@
import { CascadeRelationItem, RelationHierarchy } from "../types/Entity"; import { CascadeRelationItem, RelationHierarchy, EntityDict } from "../types/Entity";
export declare type GenericRelation = 'owner'; export declare type GenericRelation = 'owner';
export declare function convertHierarchyToAuth<R extends string>(hierarchy: RelationHierarchy<R>): { export declare function convertHierarchyToAuth<ED extends EntityDict, T extends keyof ED>(entity: T, hierarchy: RelationHierarchy<NonNullable<ED[T]['Relation']>>): {
[K in R]?: CascadeRelationItem; [K in NonNullable<ED[T]['Relation']>]?: CascadeRelationItem;
}; };

View File

@ -2,7 +2,7 @@
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.convertHierarchyToAuth = void 0; exports.convertHierarchyToAuth = void 0;
var tslib_1 = require("tslib"); var tslib_1 = require("tslib");
function convertHierarchyToAuth(hierarchy) { function convertHierarchyToAuth(entity, hierarchy) {
var e_1, _a; var e_1, _a;
var _b; var _b;
var reverseHierarchy = {}; var reverseHierarchy = {};

View File

@ -7,7 +7,7 @@ var modi_1 = require("../store/modi");
function createDynamicCheckers(schema, authDict) { function createDynamicCheckers(schema, authDict) {
var checkers = []; var checkers = [];
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false)); checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRemoveCheckers)(schema)), false)); checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRemoveCheckers)(schema, authDict)), false));
if (authDict) { if (authDict) {
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false)); checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
} }

View File

@ -100,20 +100,27 @@ var CascadeStore = /** @class */ (function (_super) {
} }
}); });
}; };
var entityIds = (0, lodash_1.uniq)(result.filter(function (ele) { return ele.entity === attr; }).map(function (ele) { return ele.entityId; })); var entityIds = (0, lodash_1.uniq)(result.filter(function (ele) { return ele.entity === attr; }).map(function (ele) {
var subRows = cascadeSelectFn.call(_this, attr, { (0, assert_1.default)(ele.entityId !== null);
data: projection2[attr], return ele.entityId;
filter: { }));
id: { if (entityIds.length > 0) {
$in: entityIds var subRows = cascadeSelectFn.call(_this, attr, {
data: projection2[attr],
filter: {
id: {
$in: entityIds
},
}, },
}, }, context, option);
}, context, option); if (subRows instanceof Promise) {
if (subRows instanceof Promise) { return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); });
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); }
else {
dealWithSubRows(subRows);
}
} }
else { else {
dealWithSubRows(subRows);
} }
}); });
} }
@ -191,18 +198,20 @@ var CascadeStore = /** @class */ (function (_super) {
}); });
}; };
var ids = (0, lodash_1.uniq)(result.filter(function (ele) { return !!(ele["".concat(attr, "Id")]); }).map(function (ele) { return ele["".concat(attr, "Id")]; })); var ids = (0, lodash_1.uniq)(result.filter(function (ele) { return !!(ele["".concat(attr, "Id")]); }).map(function (ele) { return ele["".concat(attr, "Id")]; }));
var subRows = cascadeSelectFn.call(_this, relation, { if (ids.length > 0) {
data: projection2[attr], var subRows = cascadeSelectFn.call(_this, relation, {
filter: { data: projection2[attr],
id: { filter: {
$in: ids id: {
$in: ids
},
}, },
}, }, context, option);
}, context, option); if (subRows instanceof Promise) {
if (subRows instanceof Promise) { return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); });
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); }
dealWithSubRows(subRows);
} }
dealWithSubRows(subRows);
}); });
} }
} }
@ -261,21 +270,23 @@ var CascadeStore = /** @class */ (function (_super) {
_a)); _a));
}); });
}; };
var subRows = cascadeSelectFn.call(_this, entity2_1, { if (ids.length > 0) {
data: subProjection_1, var subRows = cascadeSelectFn.call(_this, entity2_1, {
filter: (0, filter_1.combineFilters)([(_a = {}, data: subProjection_1,
_a[foreignKey_1] = { filter: (0, filter_1.combineFilters)([(_a = {},
$in: ids, _a[foreignKey_1] = {
}, $in: ids,
_a), subFilter_1]), },
sorter: subSorter_1, _a), subFilter_1]),
indexFrom: indexFrom_1, sorter: subSorter_1,
count: count_1 indexFrom: indexFrom_1,
}, context, option); count: count_1
if (subRows instanceof Promise) { }, context, option);
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); if (subRows instanceof Promise) {
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); });
}
dealWithSubRows(subRows);
} }
dealWithSubRows(subRows);
}); });
} }
} }
@ -329,22 +340,24 @@ var CascadeStore = /** @class */ (function (_super) {
_a)); _a));
}); });
}; };
var subRows = cascadeSelectFn.call(_this, entity2_1, { if (ids.length > 0) {
data: subProjection_1, var subRows = cascadeSelectFn.call(_this, entity2_1, {
filter: (0, filter_1.combineFilters)([{ data: subProjection_1,
entity: entity, filter: (0, filter_1.combineFilters)([{
entityId: { entity: entity,
$in: ids, entityId: {
} $in: ids,
}, subFilter_1]), }
sorter: subSorter_1, }, subFilter_1]),
indexFrom: indexFrom_1, sorter: subSorter_1,
count: count_1 indexFrom: indexFrom_1,
}, context, option); count: count_1
if (subRows instanceof Promise) { }, context, option);
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); if (subRows instanceof Promise) {
return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); });
}
dealWithSubRows(subRows);
} }
dealWithSubRows(subRows);
}); });
} }
} }
@ -708,11 +721,11 @@ var CascadeStore = /** @class */ (function (_super) {
*/ */
CascadeStore.prototype.doUpdateSingleRowAsync = function (entity, operation, context, option) { CascadeStore.prototype.doUpdateSingleRowAsync = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__awaiter(this, void 0, void 0, function () {
var data, action, operId, filter, now, _a, modiCreate, result_1, createInner, multipleCreate, data_1, data_1_1, d, createSingleOper, e_2_1, operatorId, createOper, _b, ids_1, selection, rows, modiUpsert, upsertModis, _c, originData, originId, createOper, updateAttrCount, result; var data, action, operId, filter, now, _a, modiCreate, result_1, createInner, multipleCreate, data_1, data_1_1, d, createSingleOper, e_2_1, operatorId, createOper, _b, ids_1, selection, rows, modiUpsert, upsertModis, _c, originData, originId, _d, createOper, updateAttrCount, result;
var e_2, _d, _e, _f, _g, _h, _j, _k, _l, _m; var e_2, _e, _f, _g, _h, _j, _k, _l;
var _this = this; var _this = this;
return tslib_1.__generator(this, function (_o) { return tslib_1.__generator(this, function (_m) {
switch (_o.label) { switch (_m.label) {
case 0: case 0:
data = operation.data, action = operation.action, operId = operation.id, filter = operation.filter; data = operation.data, action = operation.action, operId = operation.id, filter = operation.filter;
now = Date.now(); now = Date.now();
@ -746,7 +759,7 @@ var CascadeStore = /** @class */ (function (_super) {
}; };
return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiCreate, context, option)]; return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiCreate, context, option)];
case 2: case 2:
_o.sent(); _m.sent();
return [2 /*return*/, 1]; return [2 /*return*/, 1];
case 3: case 3:
result_1 = 0; result_1 = 0;
@ -836,12 +849,12 @@ var CascadeStore = /** @class */ (function (_super) {
if (!multipleCreate) return [3 /*break*/, 5]; if (!multipleCreate) return [3 /*break*/, 5];
return [4 /*yield*/, createInner(operation)]; return [4 /*yield*/, createInner(operation)];
case 4: case 4:
_o.sent(); _m.sent();
return [3 /*break*/, 12]; return [3 /*break*/, 12];
case 5: case 5:
_o.trys.push([5, 10, 11, 12]); _m.trys.push([5, 10, 11, 12]);
data_1 = tslib_1.__values(data), data_1_1 = data_1.next(); data_1 = tslib_1.__values(data), data_1_1 = data_1.next();
_o.label = 6; _m.label = 6;
case 6: case 6:
if (!!data_1_1.done) return [3 /*break*/, 9]; if (!!data_1_1.done) return [3 /*break*/, 9];
d = data_1_1.value; d = data_1_1.value;
@ -852,27 +865,27 @@ var CascadeStore = /** @class */ (function (_super) {
}; };
return [4 /*yield*/, createInner(createSingleOper)]; return [4 /*yield*/, createInner(createSingleOper)];
case 7: case 7:
_o.sent(); _m.sent();
_o.label = 8; _m.label = 8;
case 8: case 8:
data_1_1 = data_1.next(); data_1_1 = data_1.next();
return [3 /*break*/, 6]; return [3 /*break*/, 6];
case 9: return [3 /*break*/, 12]; case 9: return [3 /*break*/, 12];
case 10: case 10:
e_2_1 = _o.sent(); e_2_1 = _m.sent();
e_2 = { error: e_2_1 }; e_2 = { error: e_2_1 };
return [3 /*break*/, 12]; return [3 /*break*/, 12];
case 11: case 11:
try { try {
if (data_1_1 && !data_1_1.done && (_d = data_1.return)) _d.call(data_1); if (data_1_1 && !data_1_1.done && (_e = data_1.return)) _e.call(data_1);
} }
finally { if (e_2) throw e_2.error; } finally { if (e_2) throw e_2.error; }
return [7 /*endfinally*/]; return [7 /*endfinally*/];
case 12: return [3 /*break*/, 15]; case 12: return [3 /*break*/, 15];
case 13: return [4 /*yield*/, createInner(operation)]; case 13: return [4 /*yield*/, createInner(operation)];
case 14: case 14:
_o.sent(); _m.sent();
_o.label = 15; _m.label = 15;
case 15: case 15:
if (!option.dontCollect) { if (!option.dontCollect) {
context.opRecords.push({ context.opRecords.push({
@ -886,20 +899,20 @@ var CascadeStore = /** @class */ (function (_super) {
(0, assert_1.default)(operId); (0, assert_1.default)(operId);
return [4 /*yield*/, context.getCurrentUserId(true)]; return [4 /*yield*/, context.getCurrentUserId(true)];
case 16: case 16:
operatorId = _o.sent(); operatorId = _m.sent();
if (!operatorId) return [3 /*break*/, 22]; if (!operatorId) return [3 /*break*/, 22];
_e = { _f = {
id: 'dummy', id: 'dummy',
action: 'create' action: 'create'
}; };
_f = { _g = {
id: operId, id: operId,
action: action, action: action,
data: data, data: data,
operatorId: operatorId operatorId: operatorId
}; };
if (!(data instanceof Array)) return [3 /*break*/, 18]; if (!(data instanceof Array)) return [3 /*break*/, 18];
_g = { _h = {
id: 'dummy', id: 'dummy',
action: 'create' action: 'create'
}; };
@ -918,34 +931,34 @@ var CascadeStore = /** @class */ (function (_super) {
}); });
}); }))]; }); }))];
case 17: case 17:
_b = (_g.data = _o.sent(), _b = (_h.data = _m.sent(),
_g); _h);
return [3 /*break*/, 20]; return [3 /*break*/, 20];
case 18: case 18:
_h = { _j = {
id: 'dummy', id: 'dummy',
action: 'create' action: 'create'
}; };
_j = {}; _k = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()]; return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 19: case 19:
_b = [(_h.data = (_j.id = _o.sent(), _b = [(_j.data = (_k.id = _m.sent(),
_j.entity = entity, _k.entity = entity,
_j.entityId = data.id, _k.entityId = data.id,
_j), _k),
_h)]; _j)];
_o.label = 20; _m.label = 20;
case 20: case 20:
createOper = (_e.data = (_f.operEntity$oper = _b, createOper = (_f.data = (_g.operEntity$oper = _b,
_f), _g),
_e); _f);
return [4 /*yield*/, this.cascadeUpdateAsync('oper', createOper, context, { return [4 /*yield*/, this.cascadeUpdateAsync('oper', createOper, context, {
dontCollect: true, dontCollect: true,
dontCreateOper: true, dontCreateOper: true,
})]; })];
case 21: case 21:
_o.sent(); _m.sent();
_o.label = 22; _m.label = 22;
case 22: return [2 /*return*/, result_1]; case 22: return [2 /*return*/, result_1];
case 23: case 23:
ids_1 = (0, filter_2.getRelevantIds)(filter); ids_1 = (0, filter_2.getRelevantIds)(filter);
@ -962,9 +975,9 @@ var CascadeStore = /** @class */ (function (_super) {
dontCollect: true, dontCollect: true,
})]; })];
case 24: case 24:
rows = _o.sent(); rows = _m.sent();
ids_1.push.apply(ids_1, tslib_1.__spreadArray([], tslib_1.__read((rows.map(function (ele) { return ele.id; }))), false)); ids_1.push.apply(ids_1, tslib_1.__spreadArray([], tslib_1.__read((rows.map(function (ele) { return ele.id; }))), false));
_o.label = 25; _m.label = 25;
case 25: case 25:
if (data) { if (data) {
this.preProcessDataUpdated(data); this.preProcessDataUpdated(data);
@ -1003,7 +1016,7 @@ var CascadeStore = /** @class */ (function (_super) {
count: 1, count: 1,
}, context, option)]; }, context, option)];
case 26: case 26:
upsertModis = _o.sent(); upsertModis = _m.sent();
if (upsertModis.length > 0) { if (upsertModis.length > 0) {
_c = upsertModis[0], originData = _c.data, originId = _c.id; _c = upsertModis[0], originData = _c.data, originId = _c.id;
modiUpsert = { modiUpsert = {
@ -1017,24 +1030,26 @@ var CascadeStore = /** @class */ (function (_super) {
} }
}; };
} }
_o.label = 27; _m.label = 27;
case 27: case 27:
if (!!modiUpsert) return [3 /*break*/, 29]; if (!!modiUpsert) return [3 /*break*/, 29];
_k = { modiUpsert = {
id: 'dummy', id: 'dummy',
action: 'create' action: 'create',
data: {
id: operId,
targetEntity: entity,
entity: option.modiParentEntity,
entityId: option.modiParentId,
action: action,
data: data,
iState: 'active',
filter: filter,
},
}; };
if (!(ids_1.length > 0)) return [3 /*break*/, 29];
_d = modiUpsert.data;
_l = { _l = {
id: operId,
targetEntity: entity,
entity: option.modiParentEntity,
entityId: option.modiParentId,
action: action,
data: data,
iState: 'active',
filter: filter
};
_m = {
id: 'dummy', id: 'dummy',
action: 'create' action: 'create'
}; };
@ -1053,14 +1068,12 @@ var CascadeStore = /** @class */ (function (_super) {
}); });
}); }))]; }); }))];
case 28: case 28:
modiUpsert = (_k.data = (_l.modiEntity$modi = (_m.data = _o.sent(), _d.modiEntity$modi = (_l.data = _m.sent(),
_m), _l);
_l), _m.label = 29;
_k);
_o.label = 29;
case 29: return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiUpsert, context, option)]; case 29: return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiUpsert, context, option)];
case 30: case 30:
_o.sent(); _m.sent();
return [2 /*return*/, 1]; return [2 /*return*/, 1];
case 31: case 31:
createOper = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { createOper = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
@ -1155,15 +1168,15 @@ var CascadeStore = /** @class */ (function (_super) {
return [4 /*yield*/, createOper()]; return [4 /*yield*/, createOper()];
case 34: case 34:
// 如果不是update动作而是用户自定义的动作这里还是要记录oper // 如果不是update动作而是用户自定义的动作这里还是要记录oper
_o.sent(); _m.sent();
return [2 /*return*/, 0]; return [2 /*return*/, 0];
case 35: return [2 /*return*/, 0]; case 35: return [2 /*return*/, 0];
case 36: return [4 /*yield*/, this.updateAbjointRowAsync(entity, operation, context, option)]; case 36: return [4 /*yield*/, this.updateAbjointRowAsync(entity, operation, context, option)];
case 37: case 37:
result = _o.sent(); result = _m.sent();
return [4 /*yield*/, createOper()]; return [4 /*yield*/, createOper()];
case 38: case 38:
_o.sent(); _m.sent();
return [2 /*return*/, result]; return [2 /*return*/, result];
} }
}); });

View File

@ -23,4 +23,4 @@ export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDic
* @returns * @returns
* 使trigger来处理其相关联的外键对象trigger写作beforechecker之前执行 * 使trigger来处理其相关联的外键对象trigger写作beforechecker之前执行
*/ */
export declare function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[]; export declare function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];

View File

@ -5,10 +5,12 @@ var tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert")); var assert_1 = tslib_1.__importDefault(require("assert"));
var filter_1 = require("../store/filter"); var filter_1 = require("../store/filter");
var Exception_1 = require("../types/Exception"); var Exception_1 = require("../types/Exception");
var types_1 = require("../types");
var actionDef_1 = require("./actionDef"); var actionDef_1 = require("./actionDef");
var string_1 = require("../utils/string"); var string_1 = require("../utils/string");
var lodash_1 = require("../utils/lodash"); var lodash_1 = require("../utils/lodash");
var relation_1 = require("./relation"); var relation_1 = require("./relation");
var uuid_1 = require("../utils/uuid");
function translateCheckerInAsyncContext(checker) { function translateCheckerInAsyncContext(checker) {
var _this = this; var _this = this;
var entity = checker.entity, type = checker.type, action = checker.action; var entity = checker.entity, type = checker.type, action = checker.action;
@ -406,12 +408,12 @@ function createAuthCheckers(schema, authDict) {
var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth; var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
if (relationAuth) { if (relationAuth) {
var raFilterMakerDict_1 = {}; var raFilterMakerDict_1 = {};
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
for (var r in relationAuth) { for (var r in relationAuth) {
Object.assign(raFilterMakerDict_1, (_a = {}, Object.assign(raFilterMakerDict_1, (_a = {},
_a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity), _a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
_a)); _a));
} }
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
var entityIdAttr_1 = "".concat(entity, "Id"); var entityIdAttr_1 = "".concat(entity, "Id");
checkers.push({ checkers.push({
entity: userEntityName_1, entity: userEntityName_1,
@ -508,7 +510,7 @@ exports.createAuthCheckers = createAuthCheckers;
* @returns * @returns
* 如果有的对象允许删除需要使用trigger来处理其相关联的外键对象这些trigger写作before则会在checker之前执行仍然可以删除成功 * 如果有的对象允许删除需要使用trigger来处理其相关联的外键对象这些trigger写作before则会在checker之前执行仍然可以删除成功
*/ */
function createRemoveCheckers(schema) { function createRemoveCheckers(schema, authDict) {
var e_1, _a; var e_1, _a;
var checkers = []; var checkers = [];
// 先建立所有的一对多的关系 // 先建立所有的一对多的关系
@ -575,7 +577,7 @@ function createRemoveCheckers(schema) {
var e_3, _a, e_4, _b; var e_3, _a, e_4, _b;
var promises = []; var promises = [];
if (OneToManyMatrix[entity]) { if (OneToManyMatrix[entity]) {
var _loop_4 = function (otm) { var _loop_5 = function (otm) {
var _g, _h, _j, _k; var _g, _h, _j, _k;
var _l = tslib_1.__read(otm, 2), e = _l[0], attr = _l[1]; var _l = tslib_1.__read(otm, 2), e = _l[0], attr = _l[1];
var proj = (_g = { var proj = (_g = {
@ -627,7 +629,7 @@ function createRemoveCheckers(schema) {
try { 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_3 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
var otm = _d.value; var otm = _d.value;
_loop_4(otm); _loop_5(otm);
} }
} }
catch (e_3_1) { e_3 = { error: e_3_1 }; } catch (e_3_1) { e_3 = { error: e_3_1 }; }
@ -639,7 +641,7 @@ function createRemoveCheckers(schema) {
} }
} }
if (OneToManyOnEntityMatrix[entity]) { if (OneToManyOnEntityMatrix[entity]) {
var _loop_5 = function (otm) { var _loop_6 = function (otm) {
var _o, _p, _q; var _o, _p, _q;
var proj = { var proj = {
id: 1, id: 1,
@ -690,7 +692,7 @@ function createRemoveCheckers(schema) {
try { 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_4 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
var otm = _f.value; var otm = _f.value;
_loop_5(otm); _loop_6(otm);
} }
} }
catch (e_4_1) { e_4 = { error: e_4_1 }; } catch (e_4_1) { e_4 = { error: e_4_1 }; }
@ -720,6 +722,146 @@ function createRemoveCheckers(schema) {
} }
finally { if (e_1) throw e_1.error; } finally { if (e_1) throw e_1.error; }
} }
var _loop_4 = function (entity) {
var e_5, _b;
var cascadeRemove = authDict[entity].cascadeRemove;
if (cascadeRemove) {
var entitiesOnEntityAttr = [];
var hasAllEntity = false;
var _loop_7 = function (attr) {
if (attr === '@entity') {
hasAllEntity = true;
return "continue";
}
var rel = (0, relation_1.judgeRelation)(schema, entity, attr);
if (rel === 2) {
entitiesOnEntityAttr.push(attr);
checkers.push({
entity: attr,
action: 'remove',
type: 'logical',
priority: types_1.REMOVE_CASCADE_PRIORITY,
checker: function (operation, context) {
var _a, _b;
var filter = operation.filter;
if (cascadeRemove[attr] === 'remove') {
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'remove',
data: {},
filter: filter ? (_a = {},
_a[attr] = filter,
_a) : undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'update',
data: {
entity: null,
entityId: null,
},
filter: filter ? (_b = {},
_b[attr] = filter,
_b) : undefined,
}, { dontCollect: true });
}
});
}
else {
(0, assert_1.default)(typeof rel === 'string');
checkers.push({
entity: rel,
action: 'remove',
type: 'logical',
priority: types_1.REMOVE_CASCADE_PRIORITY,
checker: function (operation, context) {
var _a, _b, _c;
var filter = operation.filter;
if (cascadeRemove[attr] === 'remove') {
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'remove',
data: {},
filter: filter ? (_a = {},
_a[attr] = filter,
_a) : undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'update',
data: (_b = {},
_b["".concat(attr, "Id")] = null,
_b),
filter: filter ? (_c = {},
_c[attr] = filter,
_c) : undefined,
}, { dontCollect: true });
}
});
}
};
for (var attr in cascadeRemove) {
_loop_7(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) {
checkers.push({
entity: e,
action: 'remove',
type: 'logical',
priority: types_1.REMOVE_CASCADE_PRIORITY,
checker: function (operation, context) {
var _a, _b;
var filter = operation.filter;
if (cascadeRemove['@entity'] === 'remove') {
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'remove',
data: {},
filter: filter ? (_a = {},
_a[e] = filter,
_a) : undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: (0, uuid_1.generateNewId)(),
action: 'update',
data: {
entity: null,
entityId: null,
},
filter: filter ? (_b = {},
_b[e] = filter,
_b) : undefined,
}, { dontCollect: true });
}
});
};
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()) {
var e = restEntities_1_1.value;
_loop_8(e);
}
}
catch (e_5_1) { e_5 = { error: e_5_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; }
}
}
}
};
// 注入声明的cascade删除时的外键处理动作
for (var entity in authDict) {
_loop_4(entity);
}
return checkers; return checkers;
} }
exports.createRemoveCheckers = createRemoveCheckers; exports.createRemoveCheckers = createRemoveCheckers;

View File

@ -13,6 +13,8 @@ export declare type ActionDictOfEntityDict<E extends EntityDict> = {
[A in keyof E[T]['OpSchema']]?: ActionDef<string, string>; [A in keyof E[T]['OpSchema']]?: ActionDef<string, string>;
}; };
}; };
export declare type CascadeActionItem = CascadeRelationItem;
export declare type CascadeActionAuth<A extends Action = ''> = { export declare type CascadeActionAuth<A extends Action = ''> = {
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; [K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[];
}; };
export declare type ActionOnRemove = 'setNull' | 'remove';

5
lib/types/Auth.d.ts vendored
View File

@ -1,4 +1,4 @@
import { CascadeActionAuth, CascadeRelationAuth } from "."; import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
@ -60,6 +60,9 @@ export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt exten
export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = { export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>; relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
actionAuth?: CascadeActionAuth<ED[T]['Action']>; actionAuth?: CascadeActionAuth<ED[T]['Action']>;
cascadeRemove?: {
[E in keyof ED[T]['OpSchema'] | '@entity']?: ActionOnRemove;
};
}; };
export declare type AuthDefDict<ED extends EntityDict> = { export declare type AuthDefDict<ED extends EntityDict> = {
[K in keyof ED]?: AuthDef<ED, K>; [K in keyof ED]?: AuthDef<ED, K>;

View File

@ -1,6 +1,6 @@
{ {
"name": "oak-domain", "name": "oak-domain",
"version": "2.5.1", "version": "2.5.2",
"author": { "author": {
"name": "XuChang" "name": "XuChang"
}, },

View File

@ -1,28 +1,28 @@
import { CascadeRelationItem, RelationHierarchy } from "../types/Entity"; import { CascadeRelationItem, RelationHierarchy, EntityDict } from "../types/Entity";
export type GenericRelation = 'owner'; export type GenericRelation = 'owner';
export function convertHierarchyToAuth<R extends string>(hierarchy: RelationHierarchy<R>): { export function convertHierarchyToAuth<ED extends EntityDict, T extends keyof ED>(entity: T, hierarchy: RelationHierarchy<NonNullable<ED[T]['Relation']>>): {
[K in R]?: CascadeRelationItem; [K in NonNullable<ED[T]['Relation']>]?: CascadeRelationItem;
} { } {
const reverseHierarchy: RelationHierarchy<R> = {}; const reverseHierarchy: RelationHierarchy<NonNullable<ED[T]['Relation']>> = {};
for (const r in hierarchy) { for (const r in hierarchy) {
for (const r2 of hierarchy[r]!) { for (const r2 of hierarchy[r as NonNullable<ED[T]['Relation']>]!) {
if (reverseHierarchy[r2]) { if (reverseHierarchy[r2]) {
reverseHierarchy[r2]?.push(r); reverseHierarchy[r2]?.push(r as NonNullable<ED[T]['Relation']>);
} }
else { else {
reverseHierarchy[r2] = [r]; reverseHierarchy[r2] = [r as NonNullable<ED[T]['Relation']>];
} }
} }
} }
const result: { const result: {
[K in R]?: CascadeRelationItem; [K in NonNullable<ED[T]['Relation']>]?: CascadeRelationItem;
} = {}; } = {};
for (const r in reverseHierarchy) { for (const r in reverseHierarchy) {
result[r] = { result[r as NonNullable<ED[T]['Relation']>] = {
cascadePath: '', cascadePath: '',
relations: reverseHierarchy[r], relations: reverseHierarchy[r as NonNullable<ED[T]['Relation']>],
}; };
} }

View File

@ -8,7 +8,7 @@ import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefD
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){ export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){
const checkers: Checker<ED, keyof ED, Cxt>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema)); checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
checkers.push(...createRemoveCheckers<ED, Cxt>(schema)); checkers.push(...createRemoveCheckers<ED, Cxt>(schema, authDict));
if (authDict) { if (authDict) {
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict)); checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
} }

View File

@ -169,24 +169,32 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
const entityIds = uniq(result.filter( const entityIds = uniq(result.filter(
ele => ele.entity === attr ele => ele.entity === attr
).map( ).map(
ele => ele.entityId ele => {
assert(ele.entityId !== null);
return ele.entityId;
}
) as string[]); ) as string[]);
const subRows = cascadeSelectFn.call(this, attr as any, { if (entityIds.length > 0) {
data: projection2[attr], const subRows = cascadeSelectFn.call(this, attr as any, {
filter: { data: projection2[attr],
id: { filter: {
$in: entityIds id: {
}, $in: entityIds
} as any, },
}, context, option); } as any,
if (subRows instanceof Promise) { }, context, option);
return subRows.then( if (subRows instanceof Promise) {
(subRowss) => dealWithSubRows(subRowss) return subRows.then(
) (subRowss) => dealWithSubRows(subRowss)
)
}
else {
dealWithSubRows(subRows as any);
}
} }
else { else {
dealWithSubRows(subRows as any);
} }
} }
); );
@ -286,20 +294,22 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
ele => ele[`${attr}Id`] ele => ele[`${attr}Id`]
) as string[]); ) as string[]);
const subRows = cascadeSelectFn.call(this, relation, { if (ids.length > 0) {
data: projection2[attr], const subRows = cascadeSelectFn.call(this, relation, {
filter: { data: projection2[attr],
id: { filter: {
$in: ids id: {
}, $in: ids
} as any, },
}, context, option); } as any,
if (subRows instanceof Promise) { }, context, option);
return subRows.then( if (subRows instanceof Promise) {
(subRowss) => dealWithSubRows(subRowss) return subRows.then(
); (subRowss) => dealWithSubRows(subRowss)
);
}
dealWithSubRows(subRows as any);
} }
dealWithSubRows(subRows as any);
} }
); );
} }
@ -370,23 +380,25 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
); );
}; };
const subRows = cascadeSelectFn.call(this, entity2, { if (ids.length > 0) {
data: subProjection, const subRows = cascadeSelectFn.call(this, entity2, {
filter: combineFilters([{ data: subProjection,
[foreignKey]: { filter: combineFilters([{
$in: ids, [foreignKey]: {
} $in: ids,
}, subFilter]), }
sorter: subSorter, }, subFilter]),
indexFrom, sorter: subSorter,
count indexFrom,
}, context, option); count
if (subRows instanceof Promise) { }, context, option);
return subRows.then( if (subRows instanceof Promise) {
(subRowss) => dealWithSubRows(subRowss) return subRows.then(
); (subRowss) => dealWithSubRows(subRowss)
);
}
dealWithSubRows(subRows as any);
} }
dealWithSubRows(subRows as any);
} }
); );
} }
@ -452,24 +464,26 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
); );
}; };
const subRows = cascadeSelectFn.call(this, entity2, { if (ids.length > 0) {
data: subProjection, const subRows = cascadeSelectFn.call(this, entity2, {
filter: combineFilters([{ data: subProjection,
entity, filter: combineFilters([{
entityId: { entity,
$in: ids, entityId: {
} $in: ids,
}, subFilter]), }
sorter: subSorter, }, subFilter]),
indexFrom, sorter: subSorter,
count indexFrom,
}, context, option); count
if (subRows instanceof Promise) { }, context, option);
return subRows.then( if (subRows instanceof Promise) {
(subRowss) => dealWithSubRows(subRowss) return subRows.then(
); (subRowss) => dealWithSubRows(subRowss)
);
}
dealWithSubRows(subRows as any);
} }
dealWithSubRows(subRows as any);
} }
); );
} }
@ -1117,21 +1131,23 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
data, data,
iState: 'active', iState: 'active',
filter, filter,
modiEntity$modi: {
id: 'dummy',
action: 'create',
data: await Promise.all(
ids.map(
async (id) => ({
id: await generateNewIdAsync(),
entity: entity as string,
entityId: id,
})
)
),
},
}, },
}; };
if (ids.length > 0){
modiUpsert.data.modiEntity$modi = {
id: 'dummy',
action: 'create',
data: await Promise.all(
ids.map(
async (id) => ({
id: await generateNewIdAsync(),
entity: entity as string,
entityId: id,
})
)
),
};
}
} }
await this.cascadeUpdateAsync('modi', modiUpsert!, context, option); await this.cascadeUpdateAsync('modi', modiUpsert!, context, option);
return 1; return 1;

View File

@ -3,7 +3,7 @@ import { addFilterSegment, checkFilterContains, combineFilters } from "../store/
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception'; import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import { import {
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn, AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY
} from "../types"; } from "../types";
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from "./AsyncRowStore"; import { AsyncContext } from "./AsyncRowStore";
@ -12,6 +12,7 @@ import { SyncContext } from './SyncRowStore';
import { firstLetterUpperCase } from '../utils/string'; import { firstLetterUpperCase } from '../utils/string';
import { union, uniq, difference } from '../utils/lodash'; import { union, uniq, difference } from '../utils/lodash';
import { judgeRelation } from './relation'; import { judgeRelation } from './relation';
import { generateNewId } from '../utils/uuid';
export function translateCheckerInAsyncContext< export function translateCheckerInAsyncContext<
ED extends EntityDict & BaseEntityDict, ED extends EntityDict & BaseEntityDict,
@ -387,12 +388,12 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
const { relationAuth, actionAuth } = authDict[entity]!; const { relationAuth, actionAuth } = authDict[entity]!;
if (relationAuth) { if (relationAuth) {
const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>; const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
const userEntityName = `user${firstLetterUpperCase(entity)}`;
for (const r in relationAuth) { for (const r in relationAuth) {
Object.assign(raFilterMakerDict, { Object.assign(raFilterMakerDict, {
[r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity), [r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
}); });
} }
const userEntityName = `user${firstLetterUpperCase(entity)}`;
const entityIdAttr = `${entity}Id`; const entityIdAttr = `${entity}Id`;
checkers.push({ checkers.push({
entity: userEntityName as keyof ED, entity: userEntityName as keyof ED,
@ -490,7 +491,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
* @returns * @returns
* 使trigger来处理其相关联的外键对象trigger写作beforechecker之前执行 * 使trigger来处理其相关联的外键对象trigger写作beforechecker之前执行
*/ */
export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>) { export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>) {
const checkers: Checker<ED, keyof ED, Cxt>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
// 先建立所有的一对多的关系 // 先建立所有的一对多的关系
@ -657,5 +658,125 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
}) })
} }
// 注入声明的cascade删除时的外键处理动作
for (const entity in authDict) {
const { cascadeRemove } = authDict[entity]!;
if (cascadeRemove) {
const entitiesOnEntityAttr = [] as Array<keyof ED>;
let hasAllEntity = false;
for (const attr in cascadeRemove) {
if (attr === '@entity') {
hasAllEntity = true;
continue;
}
const rel = judgeRelation(schema, entity, attr);
if (rel === 2) {
entitiesOnEntityAttr.push(attr);
checkers.push({
entity: attr,
action: 'remove',
type: 'logical',
priority: REMOVE_CASCADE_PRIORITY, // 这个checker必须在检查外键不为空的checker之前执行否则无法完成
checker: (operation, context) => {
const { filter } = operation;
if (cascadeRemove[attr] === 'remove') {
return context.operate(entity, {
id: generateNewId(),
action: 'remove',
data: {},
filter: filter ? {
[attr]: filter,
}: undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: generateNewId(),
action: 'update',
data: {
entity: null,
entityId: null,
},
filter: filter ? {
[attr]: filter,
}: undefined,
}, { dontCollect: true });
}
});
}
else {
assert(typeof rel === 'string');
checkers.push({
entity: rel,
action: 'remove',
type: 'logical',
priority: REMOVE_CASCADE_PRIORITY, // 这个checker必须在检查外键不为空的checker之前执行否则无法完成
checker: (operation, context) => {
const { filter } = operation;
if (cascadeRemove[attr] === 'remove') {
return context.operate(entity, {
id: generateNewId(),
action: 'remove',
data: {},
filter: filter ? {
[attr]: filter,
}: undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: generateNewId(),
action: 'update',
data: {
[`${attr}Id`]: null,
},
filter: filter ? {
[attr]: filter,
}: undefined,
}, { dontCollect: true });
}
});
}
}
if (hasAllEntity) {
const { attributes } = schema[entity];
const { ref } = attributes.entity;
const restEntities = difference(ref, entitiesOnEntityAttr);
for (const e of restEntities) {
checkers.push({
entity: e,
action: 'remove',
type: 'logical',
priority: REMOVE_CASCADE_PRIORITY, // 这个checker必须在检查外键不为空的checker之前执行否则无法完成
checker: (operation, context) => {
const { filter } = operation;
if (cascadeRemove['@entity'] === 'remove') {
return context.operate(entity, {
id: generateNewId(),
action: 'remove',
data: {},
filter: filter ? {
[e]: filter,
}: undefined,
}, { dontCollect: true });
}
return context.operate(entity, {
id: generateNewId(),
action: 'update',
data: {
entity: null,
entityId: null,
},
filter: filter ? {
[e]: filter,
}: undefined,
}, { dontCollect: true });
}
});
}
}
}
}
return checkers; return checkers;
} }

View File

@ -17,7 +17,11 @@ export type ActionDictOfEntityDict<E extends EntityDict> = {
}; };
}; };
export type CascadeActionItem = CascadeRelationItem;
// 即在cascadePath指向的对象上有relation关系。若relation为空则不限定关系 // 即在cascadePath指向的对象上有relation关系。若relation为空则不限定关系
export type CascadeActionAuth<A extends Action = ''> = { export type CascadeActionAuth<A extends Action = ''> = {
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; [K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[];
}; };
export type ActionOnRemove = 'setNull' | 'remove'; // 当外键指向的对象被remove时本对象所定义的cascade动作

View File

@ -1,4 +1,4 @@
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth } from "."; import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth, ActionOnRemove } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
@ -88,6 +88,9 @@ export type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends Async
export type AuthDef<ED extends EntityDict, T extends keyof ED> = { export type AuthDef<ED extends EntityDict, T extends keyof ED> = {
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>; relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
actionAuth?: CascadeActionAuth<ED[T]['Action']>; actionAuth?: CascadeActionAuth<ED[T]['Action']>;
cascadeRemove?: {
[E in keyof ED[T]['OpSchema'] | '@entity']?: ActionOnRemove;
}
}; };
export type AuthDefDict<ED extends EntityDict> = { export type AuthDefDict<ED extends EntityDict> = {