From 2571e6f81880d5d6309b2b08617cb3a8d8b314f6 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Sun, 5 Feb 2023 22:15:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86authDef=E4=B8=AD?= =?UTF-8?q?=E7=9A=84cascadeRemove?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/checkers/index.js | 2 +- lib/store/checker.d.ts | 2 +- lib/store/checker.js | 152 +++++++++++++++++++++++++++++++++++++++-- lib/types/Auth.d.ts | 4 +- src/checkers/index.ts | 2 +- src/store/checker.ts | 125 ++++++++++++++++++++++++++++++++- src/types/Auth.ts | 4 +- 7 files changed, 277 insertions(+), 14 deletions(-) diff --git a/lib/checkers/index.js b/lib/checkers/index.js index 497eee8..4ef4b19 100644 --- a/lib/checkers/index.js +++ b/lib/checkers/index.js @@ -7,7 +7,7 @@ var modi_1 = require("../store/modi"); function createDynamicCheckers(schema, authDict) { 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, checker_1.createRemoveCheckers)(schema)), false)); + checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRemoveCheckers)(schema, authDict)), false)); if (authDict) { checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false)); } diff --git a/lib/store/checker.d.ts b/lib/store/checker.d.ts index 7d95350..0b1edae 100644 --- a/lib/store/checker.d.ts +++ b/lib/store/checker.d.ts @@ -23,4 +23,4 @@ export declare function createAuthCheckers | SyncContext>(schema: StorageSchema): Checker[]; +export declare function createRemoveCheckers | SyncContext>(schema: StorageSchema, authDict?: AuthDefDict): Checker[]; diff --git a/lib/store/checker.js b/lib/store/checker.js index 3a95777..23916de 100644 --- a/lib/store/checker.js +++ b/lib/store/checker.js @@ -5,10 +5,12 @@ var tslib_1 = require("tslib"); var assert_1 = tslib_1.__importDefault(require("assert")); var filter_1 = require("../store/filter"); var Exception_1 = require("../types/Exception"); +var types_1 = require("../types"); var actionDef_1 = require("./actionDef"); var string_1 = require("../utils/string"); var lodash_1 = require("../utils/lodash"); 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; @@ -508,7 +510,7 @@ exports.createAuthCheckers = createAuthCheckers; * @returns * 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功 */ -function createRemoveCheckers(schema) { +function createRemoveCheckers(schema, authDict) { var e_1, _a; var checkers = []; // 先建立所有的一对多的关系 @@ -575,7 +577,7 @@ function createRemoveCheckers(schema) { var e_3, _a, e_4, _b; var promises = []; if (OneToManyMatrix[entity]) { - var _loop_4 = function (otm) { + var _loop_5 = function (otm) { var _g, _h, _j, _k; var _l = tslib_1.__read(otm, 2), e = _l[0], attr = _l[1]; var proj = (_g = { @@ -627,7 +629,7 @@ function createRemoveCheckers(schema) { try { for (var _c = (e_3 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) { var otm = _d.value; - _loop_4(otm); + _loop_5(otm); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } @@ -639,7 +641,7 @@ function createRemoveCheckers(schema) { } } if (OneToManyOnEntityMatrix[entity]) { - var _loop_5 = function (otm) { + var _loop_6 = function (otm) { var _o, _p, _q; var proj = { id: 1, @@ -690,7 +692,7 @@ function createRemoveCheckers(schema) { try { for (var _e = (e_4 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) { var otm = _f.value; - _loop_5(otm); + _loop_6(otm); } } 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; } } + 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; } exports.createRemoveCheckers = createRemoveCheckers; diff --git a/lib/types/Auth.d.ts b/lib/types/Auth.d.ts index 5981136..3b62fd3 100644 --- a/lib/types/Auth.d.ts +++ b/lib/types/Auth.d.ts @@ -60,8 +60,8 @@ export declare type Checker = { relationAuth?: CascadeRelationAuth>; actionAuth?: CascadeActionAuth; - onCasadeRemove?: { - [E in keyof ED[T]['OpSchema'] | '@all']?: ActionOnRemove; + cascadeRemove?: { + [E in keyof ED[T]['OpSchema'] | '@entity']?: ActionOnRemove; }; }; export declare type AuthDefDict = { diff --git a/src/checkers/index.ts b/src/checkers/index.ts index 3ecb67b..840ce8b 100644 --- a/src/checkers/index.ts +++ b/src/checkers/index.ts @@ -8,7 +8,7 @@ import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefD export function createDynamicCheckers | SyncContext>(schema: StorageSchema, authDict?: AuthDefDict){ const checkers: Checker[] = []; checkers.push(...createModiRelatedCheckers(schema)); - checkers.push(...createRemoveCheckers(schema)); + checkers.push(...createRemoveCheckers(schema, authDict)); if (authDict) { checkers.push(...createAuthCheckers(schema, authDict)); } diff --git a/src/store/checker.ts b/src/store/checker.ts index 874de81..2014f41 100644 --- a/src/store/checker.ts +++ b/src/store/checker.ts @@ -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 + EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY } from "../types"; import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { AsyncContext } from "./AsyncRowStore"; @@ -12,6 +12,7 @@ import { SyncContext } from './SyncRowStore'; import { firstLetterUpperCase } from '../utils/string'; import { union, uniq, difference } from '../utils/lodash'; import { judgeRelation } from './relation'; +import { generateNewId } from '../utils/uuid'; export function translateCheckerInAsyncContext< ED extends EntityDict & BaseEntityDict, @@ -490,7 +491,7 @@ export function createAuthCheckers | SyncContext>(schema: StorageSchema) { +export function createRemoveCheckers | SyncContext>(schema: StorageSchema, authDict?: AuthDefDict) { const checkers: Checker[] = []; // 先建立所有的一对多的关系 @@ -657,5 +658,125 @@ export function createRemoveCheckers; + 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; } \ No newline at end of file diff --git a/src/types/Auth.ts b/src/types/Auth.ts index f8c6a72..27db51f 100644 --- a/src/types/Auth.ts +++ b/src/types/Auth.ts @@ -88,8 +88,8 @@ export type Checker = { relationAuth?: CascadeRelationAuth>; actionAuth?: CascadeActionAuth; - onCasadeRemove?: { - [E in keyof ED[T]['OpSchema'] | '@all']?: ActionOnRemove; + cascadeRemove?: { + [E in keyof ED[T]['OpSchema'] | '@entity']?: ActionOnRemove; } };