contextMenuFactory更改了计算逻辑,要求显式传路径

This commit is contained in:
Xu Chang 2023-07-31 16:18:22 +08:00
parent 7cbfeb2d84
commit fad31698e7
3 changed files with 114 additions and 166 deletions

View File

@ -1,5 +1,5 @@
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { EntityDict, AuthCascadePath, Aspect } from 'oak-domain/lib/types';
import { EntityDict, Aspect, AuthCascadePath } from 'oak-domain/lib/types';
import { CommonAspectDict } from 'oak-common-aspect';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
@ -12,17 +12,14 @@ interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
action: ED[T]['Action'];
paths?: string[];
}
interface IMenuWrapper<ED extends EntityDict & BaseEntityDict, T extends keyof ED> extends IMenu<ED, T> {
filtersMaker: (entity: keyof ED, entityId: string) => Array<ED[T]['Selection']['filter']>;
}
export declare class ContextMenuFactory<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>> extends Feature {
cache: Cache<ED, Cxt, FrontCxt, AD>;
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
menus?: IMenu<ED, keyof ED>[];
cascadePathGraph: AuthCascadePath<ED>[];
relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>;
private makeMenuWrappers;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]);
setMenus(menus: IMenu<ED, keyof ED>[]): void;
makeMenuFilters(destEntity: keyof ED, paths: string[], entity: keyof ED, entityId: string): (true | ED[keyof ED]["Selection"]["filter"])[];
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string): OMenu[];
}
export {};

View File

@ -17,86 +17,65 @@ var ContextMenuFactory = /** @class */ (function (_super) {
_this.relationAuth = relationAuth;
return _this;
}
ContextMenuFactory.prototype.makeMenuWrappers = function (menus) {
var _this = this;
var destEntities = (0, lodash_1.uniq)(menus.map(function (ele) { return ele.entity; }));
var pathMap = {};
this.cascadePathGraph.forEach(function (path) {
var _a;
var _b = tslib_1.__read(path, 4), destEntity = _b[0], p = _b[1], s = _b[2], ir = _b[3];
if (ir && destEntities.includes(destEntity)) {
// 应用在判定登录者身份时用的对象都是应用级对象且以relation判定关系
if (pathMap[destEntity]) {
(_a = pathMap[destEntity]) === null || _a === void 0 ? void 0 : _a.push(path);
ContextMenuFactory.prototype.setMenus = function (menus) {
(0, assert_1.default)(!this.menus, 'setMenus只应该全局调用一次');
this.menus = menus;
};
ContextMenuFactory.prototype.makeMenuFilters = function (destEntity, paths, entity, entityId) {
var schema = this.cache.getSchema();
(0, assert_1.default)(paths.length > 0);
var filters = paths.map(function (path) {
if (path === '') {
if (entity === destEntity) {
return {
id: entityId,
};
}
return;
}
var pathhh = path.split('.');
var judgeIter = function (e2, idx) {
var rel = (0, relation_1.judgeRelation)(schema, e2, pathhh[idx]);
var e3 = e2;
if (typeof rel === 'string') {
e3 = rel;
}
else if (rel === 2) {
e3 = pathhh[idx];
}
else {
pathMap[destEntity] = [path];
(0, assert_1.default)(rel instanceof Array);
e3 = rel[0];
}
}
});
return menus.map(function (menu) {
var destEntity = menu.entity, cascadePaths = menu.paths;
var filtersMaker = function (sourceEntity, entityId) {
// 在cascadePathMap中找到可能的路径并构建对应的filter
var paths = pathMap[destEntity].filter(function (ele) {
if (cascadePaths) {
(0, assert_1.default)(cascadePaths.length > 0);
return ele[2] === sourceEntity && cascadePaths.includes(ele[1]);
if (idx === pathhh.length - 1) {
if (e3 === 'user') {
// 用user连接说明一定满足
return true;
}
return ele[2] === sourceEntity;
});
return paths.map(function (path) {
var p = path[1];
if (p === '') {
return {
id: entityId,
};
if (e3 === entity) {
var filter = {};
return (0, lodash_1.set)(filter, "".concat(path, ".id"), entityId);
}
else {
var ps_1 = p.split('.');
var makeFilterInner_1 = function (entity, idx) {
var _a, _b;
var attr = ps_1[idx];
var rel = (0, relation_1.judgeRelation)(_this.cache.getSchema(), entity, attr);
if (idx === ps_1.length - 1) {
if (rel === 2) {
return {
entity: attr,
entityId: entityId,
};
}
(0, assert_1.default)(typeof rel === 'string');
return _a = {},
_a["".concat(attr, "Id")] = entityId,
_a;
}
var e = rel === 2 ? attr : rel;
return _b = {},
_b[attr] = makeFilterInner_1(e, idx + 1),
_b;
};
return makeFilterInner_1(destEntity, 0);
}
});
return undefined;
}
return judgeIter(e3, idx + 1);
};
return Object.assign({}, menu, {
filtersMaker: filtersMaker,
});
});
};
ContextMenuFactory.prototype.setMenus = function (menus) {
(0, assert_1.default)(!this.menuWrappers, 'setMenus只应该全局调用一次');
this.menuWrappers = this.makeMenuWrappers(menus);
return judgeIter(destEntity, 0);
}).filter(function (ele) { return !!ele; });
return filters;
};
ContextMenuFactory.prototype.getMenusByContext = function (entity, entityId) {
var _this = this;
(0, assert_1.default)(this.menuWrappers, '应当先调用setMenus才能动态判定菜单');
var menus = this.menuWrappers.filter(function (wrapper) {
var destEntity = wrapper.entity, filtersMaker = wrapper.filtersMaker, action = wrapper.action;
var filters = filtersMaker(entity, entityId);
(0, assert_1.default)(this.menus, '应当先调用setMenus才能动态判定菜单');
var menus = this.menus.filter(function (menu) {
var destEntity = menu.entity, paths = menu.paths, action = menu.action;
var filters = paths ? _this.makeMenuFilters(destEntity, paths, entity, entityId) : [{}]; // 如果没有path视为无法推断操作的filter直接返回无任何限制
if (filters.length > 0) {
// 这里应该是or关系paths表达的路径中只要有一条满足就可能满足
var allows = filters.map(function (filter) {
if (filter === true) {
return true;
}
// relationAuth和其它的checker现在分开判断
return _this.relationAuth.checkRelation(destEntity, {
action: action,

View File

@ -1,16 +1,14 @@
import assert from 'assert';
import { uniq, set, omit } from 'oak-domain/lib/utils/lodash';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { EntityDict, AuthCascadePath, Aspect } from 'oak-domain/lib/types';
import { EntityDict, Aspect, AuthCascadePath } from 'oak-domain/lib/types';
import { CommonAspectDict } from 'oak-common-aspect';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
import { combineFilters } from 'oak-domain/lib/store/filter';
import { Cache } from './cache';
import { Feature } from '../types/Feature';
import { judgeRelation } from 'oak-domain/lib/store/relation';
import { RelationAuth } from './relationAuth';
import { generateNewId } from 'oak-domain/lib/utils/uuid';
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
name: string;
@ -30,91 +28,10 @@ export class ContextMenuFactory<
AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>
> extends Feature {
cache: Cache<ED, Cxt, FrontCxt, AD>;
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
menus?: IMenu<ED, keyof ED>[];
cascadePathGraph: AuthCascadePath<ED>[];
relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>;
private makeMenuWrappers(menus: IMenu<ED, keyof ED>[]): IMenuWrapper<ED, keyof ED>[] {
const destEntities = uniq(
menus.map(
ele => ele.entity
)
);
const pathMap: {
[E in keyof ED]?: AuthCascadePath<ED>[];
} = {};
this.cascadePathGraph.forEach(
(path) => {
const [destEntity, p, s, ir] = path;
if (ir && destEntities.includes(destEntity)) {
// 应用在判定登录者身份时用的对象都是应用级对象且以relation判定关系
if (pathMap[destEntity]) {
pathMap[destEntity]?.push(path);
}
else {
pathMap[destEntity] = [path];
}
}
}
);
return menus.map(
(menu) => {
const { entity: destEntity, paths: cascadePaths } = menu;
const filtersMaker = (sourceEntity: keyof ED, entityId: string) => {
// 在cascadePathMap中找到可能的路径并构建对应的filter
const paths = pathMap[destEntity]!.filter(
(ele) => {
if (cascadePaths) {
assert(cascadePaths.length > 0);
return ele[2] === sourceEntity && cascadePaths.includes(ele[1]);
}
return ele[2] === sourceEntity;
}
);
return paths.map(
(path) => {
const p = path[1];
if (p === '') {
return {
id: entityId,
};
}
else {
const ps = p.split('.');
const makeFilterInner = (entity: keyof ED, idx: number): ED[keyof ED]['Selection']['filter'] => {
const attr = ps[idx];
const rel = judgeRelation(this.cache.getSchema(), entity, attr);
if (idx === ps.length - 1) {
if (rel === 2) {
return {
entity: attr,
entityId,
};
}
assert(typeof rel === 'string');
return {
[`${attr}Id`]: entityId,
};
}
const e = rel === 2 ? attr : rel as string;
return {
[attr]: makeFilterInner(e, idx + 1),
};
};
return makeFilterInner(destEntity, 0);
}
}
)
};
return Object.assign({}, menu, {
filtersMaker,
});
}
);
}
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]) {
super();
this.cache = cache;
@ -123,20 +40,75 @@ export class ContextMenuFactory<
}
setMenus(menus: IMenu<ED, keyof ED>[]) {
assert(!this.menuWrappers, 'setMenus只应该全局调用一次');
this.menuWrappers = this.makeMenuWrappers(menus);
assert(!this.menus, 'setMenus只应该全局调用一次');
this.menus = menus;
}
makeMenuFilters(destEntity: keyof ED, paths: string[], entity: keyof ED, entityId: string) {
const schema = this.cache.getSchema();
assert(paths.length > 0);
const filters = paths.map(
(path) => {
if (path === '') {
if (entity === destEntity) {
return {
id: entityId,
} as ED[keyof ED]['Selection']['filter'];
}
return;
}
const pathhh = path.split('.');
const judgeIter = (e2: keyof ED, idx: number): true | undefined | ED[keyof ED]['Selection']['filter'] => {
const rel = judgeRelation(schema, e2, pathhh[idx]);
let e3 = e2;
if (typeof rel === 'string') {
e3 = rel;
}
else if (rel === 2) {
e3 = pathhh[idx];
}
else {
assert(rel instanceof Array);
e3 = rel[0];
}
if (idx === pathhh.length - 1) {
if (e3 === 'user') {
// 用user连接说明一定满足
return true;
}
if (e3 === entity) {
const filter: ED[keyof ED]['Selection']['filter'] = {};
return set(filter, `${path}.id`, entityId);
}
return undefined;
}
return judgeIter(e3, idx + 1);
}
return judgeIter(destEntity, 0);
}
).filter(
ele => !!ele
);
return filters as (true | ED[keyof ED]['Selection']['filter'])[];
}
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string) {
assert(this.menuWrappers, '应当先调用setMenus才能动态判定菜单');
const menus = this.menuWrappers.filter(
(wrapper) => {
const { entity: destEntity, filtersMaker, action } = wrapper;
const filters = filtersMaker(entity, entityId);
assert(this.menus, '应当先调用setMenus才能动态判定菜单');
const menus = this.menus.filter(
(menu) => {
const { entity: destEntity, paths, action } = menu;
const filters = paths ? this.makeMenuFilters(destEntity, paths, entity, entityId) : [{}]; // 如果没有path视为无法推断操作的filter直接返回无任何限制
if (filters.length > 0) {
// 这里应该是or关系paths表达的路径中只要有一条满足就可能满足
const allows = filters.map(
(filter) => {
if (filter === true) {
return true;
}
// relationAuth和其它的checker现在分开判断
return this.relationAuth.checkRelation(destEntity, {
action,