适配了domain的定义性更新,重构了不少代码流程
This commit is contained in:
parent
d683d2e709
commit
ef6175313f
|
|
@ -1,22 +1,24 @@
|
|||
import { AggregationResult, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { AggregationResult, EntityDict, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { StorageSchema } from "oak-domain/lib/types/Storage";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, TxnOption } from 'oak-domain/lib/types';
|
||||
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||
import { SyncContext, SyncRowStore } from 'oak-domain/lib/store/SyncRowStore';
|
||||
interface CachStoreOperation extends TreeStoreOperateOption {
|
||||
checkerTypes?: CheckerType[];
|
||||
}
|
||||
export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt extends SyncContext<ED>> extends TreeStore<ED> implements SyncRowStore<ED, SyncContext<ED>> {
|
||||
private checkerExecutor;
|
||||
private triggerExecutor;
|
||||
private getFullDataFn?;
|
||||
private resetInitialDataFn?;
|
||||
constructor(storageSchema: StorageSchema<ED>, getFullDataFn?: () => any, resetInitialDataFn?: () => void);
|
||||
aggregate<T extends keyof ED, OP extends SelectOption>(entity: T, aggregation: ED[T]['Aggregation'], context: SyncContext<ED>, option: OP): AggregationResult<ED[T]['Schema']>;
|
||||
protected cascadeUpdate<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: SyncContext<ED>, option: OP): OperationResult<ED>;
|
||||
operate<T extends keyof ED, OP extends TreeStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
||||
protected cascadeUpdate<T extends keyof ED, OP extends CachStoreOperation>(entity: T, operation: ED[T]['Operation'], context: SyncContext<ED>, option: OP): OperationResult<ED>;
|
||||
operate<T extends keyof ED, OP extends CachStoreOperation>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
||||
sync<Cxt extends SyncContext<ED>>(opRecords: Array<OpRecord<ED>>, context: Cxt): void;
|
||||
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, checkerTypes?: CheckerType[]): void;
|
||||
select<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]["Schema"]>[];
|
||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
||||
registerGeneralChecker(type: CheckerType, fn: <T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt) => void): void;
|
||||
/**
|
||||
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
||||
* @returns
|
||||
|
|
@ -32,3 +34,4 @@ export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt exte
|
|||
commit(txnId: string): void;
|
||||
rollback(txnId: string): void;
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CacheStore = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var action_1 = require("oak-domain/lib/actions/action");
|
||||
var oak_memory_tree_store_1 = require("oak-memory-tree-store");
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var CheckerExecutor_1 = tslib_1.__importDefault(require("./CheckerExecutor"));
|
||||
var SyncTriggerExecutor_1 = tslib_1.__importDefault(require("./SyncTriggerExecutor"));
|
||||
;
|
||||
var CacheStore = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(CacheStore, _super);
|
||||
function CacheStore(storageSchema, getFullDataFn, resetInitialDataFn) {
|
||||
var _this = _super.call(this, storageSchema) || this;
|
||||
_this.checkerExecutor = new CheckerExecutor_1.default();
|
||||
_this.triggerExecutor = new SyncTriggerExecutor_1.default();
|
||||
_this.getFullDataFn = getFullDataFn;
|
||||
_this.resetInitialDataFn = resetInitialDataFn;
|
||||
return _this;
|
||||
|
|
@ -21,13 +22,16 @@ var CacheStore = /** @class */ (function (_super) {
|
|||
CacheStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
|
||||
(0, assert_1.default)(context.getCurrentTxnId());
|
||||
if (!option.blockTrigger) {
|
||||
this.checkerExecutor.check(entity, operation, context, 'before');
|
||||
this.triggerExecutor.check(entity, operation, context, 'before', option.checkerTypes);
|
||||
}
|
||||
var result = _super.prototype.cascadeUpdate.call(this, entity, operation, context, option);
|
||||
if (!option.blockTrigger) {
|
||||
this.checkerExecutor.check(entity, operation, context, 'after');
|
||||
if (operation.data) {
|
||||
var result = _super.prototype.cascadeUpdate.call(this, entity, operation, context, option);
|
||||
if (!option.blockTrigger) {
|
||||
this.triggerExecutor.check(entity, operation, context, 'after', option.checkerTypes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
return {};
|
||||
};
|
||||
CacheStore.prototype.operate = function (entity, operation, context, option) {
|
||||
(0, assert_1.default)(context.getCurrentTxnId());
|
||||
|
|
@ -56,7 +60,15 @@ var CacheStore = /** @class */ (function (_super) {
|
|||
};
|
||||
CacheStore.prototype.check = function (entity, operation, context, checkerTypes) {
|
||||
(0, assert_1.default)(context.getCurrentTxnId());
|
||||
this.checkerExecutor.check(entity, operation, context, undefined, checkerTypes);
|
||||
var action = operation.action;
|
||||
if (action_1.readOnlyActions.includes(action)) {
|
||||
// 只读查询的checker只能在根部入口执行
|
||||
this.triggerExecutor.check(entity, operation, context, undefined, checkerTypes);
|
||||
return;
|
||||
}
|
||||
this.operate(entity, operation, context, {
|
||||
checkerTypes: checkerTypes,
|
||||
});
|
||||
};
|
||||
CacheStore.prototype.select = function (entity, selection, context, option) {
|
||||
var autoCommit = !context.getCurrentTxnId();
|
||||
|
|
@ -79,11 +91,11 @@ var CacheStore = /** @class */ (function (_super) {
|
|||
return result;
|
||||
};
|
||||
CacheStore.prototype.registerChecker = function (checker) {
|
||||
this.checkerExecutor.registerChecker(checker);
|
||||
};
|
||||
CacheStore.prototype.registerGeneralChecker = function (type, fn) {
|
||||
this.checkerExecutor.registerGeneralChecker(type, fn);
|
||||
this.triggerExecutor.registerChecker(checker);
|
||||
};
|
||||
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||
this.triggerExecutor.registerTrigger(trigger);
|
||||
} */
|
||||
/**
|
||||
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
||||
* @returns
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType } from 'oak-domain/lib/types';
|
||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
export default class SyncTriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends SyncContext<ED>> {
|
||||
static All_Checker_Types: CheckerType[];
|
||||
private checkerMap;
|
||||
private addToCheckerMap;
|
||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
||||
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, when?: 'before' | 'after', checkerTypes?: CheckerType[]): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
// 简化版的对checker的同步检查
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var types_1 = require("oak-domain/lib/types");
|
||||
var checker_1 = require("oak-domain/lib/store/checker");
|
||||
var filter_1 = require("oak-domain/lib/store/filter");
|
||||
var SyncTriggerExecutor = /** @class */ (function () {
|
||||
function SyncTriggerExecutor() {
|
||||
this.checkerMap = {};
|
||||
}
|
||||
SyncTriggerExecutor.prototype.addToCheckerMap = function (action, entity, priority, when, fn, type, filter) {
|
||||
var _a, _b, _c;
|
||||
if (this.checkerMap[entity] && this.checkerMap[entity][action]) {
|
||||
var iter = 0;
|
||||
var checkers = this.checkerMap[entity][action];
|
||||
for (; iter < checkers.length; iter++) {
|
||||
if (priority <= checkers[iter].priority) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkers.splice(iter, 0, {
|
||||
type: type,
|
||||
priority: priority,
|
||||
fn: fn,
|
||||
when: when,
|
||||
filter: filter,
|
||||
});
|
||||
}
|
||||
else if (this.checkerMap[entity]) {
|
||||
Object.assign(this.checkerMap[entity], (_a = {},
|
||||
_a[action] = [{
|
||||
type: type,
|
||||
priority: priority,
|
||||
fn: fn,
|
||||
when: when,
|
||||
filter: filter,
|
||||
}],
|
||||
_a));
|
||||
}
|
||||
else {
|
||||
Object.assign(this.checkerMap, (_b = {},
|
||||
_b[entity] = (_c = {},
|
||||
_c[action] = [{
|
||||
type: type,
|
||||
priority: priority,
|
||||
fn: fn,
|
||||
when: when,
|
||||
filter: filter,
|
||||
}],
|
||||
_c),
|
||||
_b));
|
||||
}
|
||||
};
|
||||
SyncTriggerExecutor.prototype.registerChecker = function (checker) {
|
||||
var _this = this;
|
||||
var entity = checker.entity, action = checker.action, priority = checker.priority, type = checker.type, conditionalFilter = checker.conditionalFilter;
|
||||
var _a = (0, checker_1.translateCheckerInSyncContext)(checker), fn = _a.fn, when = _a.when;
|
||||
if (action instanceof Array) {
|
||||
action.forEach(function (a) { return _this.addToCheckerMap(a, entity, priority || types_1.CHECKER_PRIORITY_MAP[type], when, fn, type, conditionalFilter); });
|
||||
}
|
||||
else {
|
||||
this.addToCheckerMap(action, entity, priority, when, fn, type, conditionalFilter);
|
||||
}
|
||||
};
|
||||
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||
const {
|
||||
action,
|
||||
entity,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter,
|
||||
} = trigger as UpdateTrigger<ED, T, Cxt>;
|
||||
|
||||
if (when === 'commit') {
|
||||
return;
|
||||
}
|
||||
if (action instanceof Array) {
|
||||
action.forEach(
|
||||
(a) => this.addToCheckerMap(a, entity, priority || TRIGGER_DEFAULT_PRIORITY, when, fn as any, undefined, filter)
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.addToCheckerMap(action ,entity, priority || TRIGGER_DEFAULT_PRIORITY, when, fn as any, undefined, filter);
|
||||
}
|
||||
} */
|
||||
SyncTriggerExecutor.prototype.check = function (entity, operation, context, when, checkerTypes) {
|
||||
var e_1, _a;
|
||||
var action = operation.action;
|
||||
var checkers = this.checkerMap[entity] && this.checkerMap[entity][action];
|
||||
if (checkers) {
|
||||
var checkers2 = checkers.filter(function (ele) { return (!checkerTypes || checkerTypes.includes(ele.type)) && (!when || ele.when === when); });
|
||||
try {
|
||||
for (var checkers2_1 = tslib_1.__values(checkers2), checkers2_1_1 = checkers2_1.next(); !checkers2_1_1.done; checkers2_1_1 = checkers2_1.next()) {
|
||||
var checker = checkers2_1_1.value;
|
||||
var filter = checker.filter;
|
||||
if (filter) {
|
||||
var filterr = typeof filter === 'function' ? filter(operation, context, {}) : filter;
|
||||
(0, assert_1.default)(!(filter instanceof Promise), "\u5BF9".concat(entity, "\u7684\u52A8\u4F5C").concat(action, "\u4E0A\u5B9A\u4E49\u7684checker\uFF0C\u5176filter\u8FD4\u56DE\u4E86Promise\uFF0C\u8BF7\u6CE8\u610F\u5C06\u540C\u6B65\u548C\u5F02\u6B65\u7684\u8FD4\u56DE\u533A\u5206\u5BF9\u5F85"));
|
||||
var isRepel = (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter, true);
|
||||
(0, assert_1.default)(typeof isRepel === 'boolean');
|
||||
if (isRepel) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
checker.fn(operation, context, {});
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (checkers2_1_1 && !checkers2_1_1.done && (_a = checkers2_1.return)) _a.call(checkers2_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
};
|
||||
SyncTriggerExecutor.All_Checker_Types = ['data', 'logical', 'logicalRelation', 'relation', 'row'];
|
||||
return SyncTriggerExecutor;
|
||||
}());
|
||||
exports.default = SyncTriggerExecutor;
|
||||
|
|
@ -5,7 +5,6 @@ var tslib_1 = require("tslib");
|
|||
var node_schedule_1 = require("node-schedule");
|
||||
var constant_1 = require("../constant/constant");
|
||||
var DebugStore_1 = require("./DebugStore");
|
||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||
var assert_1 = require("oak-domain/lib/utils/assert");
|
||||
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
function initDataInStore(store, initialData, stat) {
|
||||
|
|
@ -387,9 +386,6 @@ function createDebugStore(storageSchema, contextBuilder, triggers, checkers, wat
|
|||
triggers.forEach(function (ele) { return store.registerTrigger(ele); });
|
||||
checkers.forEach(function (ele) { return store.registerChecker(ele); });
|
||||
(0, assert_1.assert)(actionDict);
|
||||
var _a = (0, actionDef_1.analyzeActionDefDict)(storageSchema, actionDict), adTriggers = _a.triggers, adCheckers = _a.checkers, adWatchers = _a.watchers;
|
||||
adTriggers.forEach(function (ele) { return store.registerTrigger(ele); });
|
||||
adCheckers.forEach(function (ele) { return store.registerChecker(ele); });
|
||||
// 如果没有物化数据则使用initialData初始化debugStore
|
||||
var data = getMaterializedData();
|
||||
if (!data) {
|
||||
|
|
@ -411,7 +407,7 @@ function createDebugStore(storageSchema, contextBuilder, triggers, checkers, wat
|
|||
materializeData(data, stat);
|
||||
}, 10000);
|
||||
// 启动watcher
|
||||
initializeWatchers(store, contextBuilder, watchers.concat(adWatchers));
|
||||
initializeWatchers(store, contextBuilder, watchers);
|
||||
// 启动timer
|
||||
if (timers) {
|
||||
initializeTimers(store, contextBuilder, timers);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
|||
}>, context: FrontCxt): void;
|
||||
private getInner;
|
||||
get<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context?: FrontCxt, allowMiss?: boolean): Partial<ED[T]["Schema"]>[];
|
||||
judgeRelation(entity: keyof ED, attr: string): string | 0 | 1 | 2 | string[];
|
||||
judgeRelation(entity: keyof ED, attr: string): string | 0 | 1 | string[] | 2;
|
||||
bindOnSync(callback: (opRecords: OpRecord<ED>[]) => void): void;
|
||||
unbindOnSync(callback: (opRecords: OpRecord<ED>[]) => void): void;
|
||||
getCachedData(): { [T in keyof ED]?: ED[T]["OpSchema"][] | undefined; };
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ var Cache = /** @class */ (function (_super) {
|
|||
var operation = {
|
||||
action: action,
|
||||
filter: filter,
|
||||
data: data,
|
||||
data: data
|
||||
};
|
||||
try {
|
||||
this.cacheStore.check(entity, operation, context, checkerTypes);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
|||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
import { Cache } from './cache';
|
||||
import { Feature } from '../types/Feature';
|
||||
import { RelationAuth } from './relationAuth';
|
||||
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
|
||||
name: string;
|
||||
entity: T;
|
||||
|
|
@ -18,8 +19,9 @@ export declare class ContextMenuFactory<ED extends EntityDict & BaseEntityDict,
|
|||
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
|
||||
cascadePathGraph: AuthCascadePath<ED>[];
|
||||
relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>;
|
||||
private makeMenuWrappers;
|
||||
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]);
|
||||
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]);
|
||||
setMenus(menus: IMenu<ED, keyof ED>[]): void;
|
||||
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string): OMenu[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ var relation_1 = require("oak-domain/lib/store/relation");
|
|||
;
|
||||
var ContextMenuFactory = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(ContextMenuFactory, _super);
|
||||
function ContextMenuFactory(cache, cascadePathGraph) {
|
||||
function ContextMenuFactory(cache, relationAuth, cascadePathGraph) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.cache = cache;
|
||||
_this.cascadePathGraph = cascadePathGraph;
|
||||
_this.relationAuth = relationAuth;
|
||||
return _this;
|
||||
}
|
||||
ContextMenuFactory.prototype.makeMenuWrappers = function (menus) {
|
||||
|
|
@ -95,7 +96,14 @@ var ContextMenuFactory = /** @class */ (function (_super) {
|
|||
var filters = filtersMaker(entity, entityId);
|
||||
if (filters.length > 0) {
|
||||
// 这里应该是or关系,paths表达的路径中只要有一条满足就可能满足
|
||||
var allows = filters.map(function (filter) { return _this.cache.checkOperation(destEntity, action, undefined, filter, ['logical', 'relation', 'logicalRelation', 'row']); });
|
||||
var allows = filters.map(function (filter) {
|
||||
// relationAuth和其它的checker现在分开判断
|
||||
return _this.relationAuth.checkRelation(destEntity, {
|
||||
action: action,
|
||||
data: undefined,
|
||||
filter: filter,
|
||||
}) && _this.cache.checkOperation(destEntity, action, undefined, filter, ['logical', 'relation', 'logicalRelation', 'row']);
|
||||
});
|
||||
if (allows.indexOf(true) >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ var geo_1 = require("./geo");
|
|||
function initialize(aspectWrapper, storageSchema, contextBuilder, store, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, colorDict, makeBridgeUrlFn) {
|
||||
var cache = new cache_1.Cache(aspectWrapper, contextBuilder, store);
|
||||
var location = new location_1.Location();
|
||||
var relationAuth = new relationAuth_1.RelationAuth(aspectWrapper, cache, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||
var relationAuth = new relationAuth_1.RelationAuth(aspectWrapper, contextBuilder, cache, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||
var runningTree = new runningTree_1.RunningTree(cache, storageSchema, relationAuth);
|
||||
var locales = new locales_1.Locales(aspectWrapper, makeBridgeUrlFn);
|
||||
var geo = new geo_1.Geo(aspectWrapper);
|
||||
|
|
@ -29,7 +29,7 @@ function initialize(aspectWrapper, storageSchema, contextBuilder, store, actionC
|
|||
var navigator = new navigator_1.Navigator();
|
||||
var port = new port_1.Port(aspectWrapper);
|
||||
var style = new style_1.Style(colorDict);
|
||||
var contextMenuFactory = new contextMenuFactory_1.ContextMenuFactory(cache, actionCascadePathGraph);
|
||||
var contextMenuFactory = new contextMenuFactory_1.ContextMenuFactory(cache, relationAuth, actionCascadePathGraph);
|
||||
return {
|
||||
cache: cache,
|
||||
location: location,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
|||
import { Cache } from './cache';
|
||||
export declare class RelationAuth<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>> extends Feature {
|
||||
private cache;
|
||||
private contextBuilder;
|
||||
private aspectWrapper;
|
||||
private actionCascadePathGraph;
|
||||
private actionCascadePathMap;
|
||||
|
|
@ -15,7 +16,7 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict, Cxt ex
|
|||
private authDeduceRelationMap;
|
||||
static IgnoredActions: string[];
|
||||
private entityGraph?;
|
||||
constructor(aspectWrapper: AspectWrapper<ED, Cxt, AD>, cache: Cache<ED, Cxt, FrontCxt, AD>, actionCascadePathGraph: AuthCascadePath<ED>[], relationCascadePathGraph: AuthCascadePath<ED>[], authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities: (keyof ED)[]);
|
||||
constructor(aspectWrapper: AspectWrapper<ED, Cxt, AD>, contextBuilder: () => FrontCxt, cache: Cache<ED, Cxt, FrontCxt, AD>, actionCascadePathGraph: AuthCascadePath<ED>[], relationCascadePathGraph: AuthCascadePath<ED>[], authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities: (keyof ED)[]);
|
||||
private judgeRelation;
|
||||
getHasRelationEntities(): string[];
|
||||
getDeduceRelationAttribute(entity: keyof ED): string | undefined;
|
||||
|
|
@ -40,6 +41,6 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict, Cxt ex
|
|||
getCascadeActionAuths(entity: keyof ED, ir: boolean): AuthCascadePath<ED>[];
|
||||
getCascadeRelationAuthsBySource(entity: keyof ED): AuthCascadePath<ED>[];
|
||||
getCascadeRelationAuths(entity: keyof ED, ir: boolean): AuthCascadePath<ED>[];
|
||||
checkRelation<T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: FrontCxt): void;
|
||||
checkRelation<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'] | ED[T]['Selection'], 'id'>): boolean;
|
||||
getRelationIdByName(entity: keyof ED, name: string, entityId?: string): Promise<ED["relation"]["Schema"]["id"] | undefined>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RelationAuth = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var types_1 = require("oak-domain/lib/types");
|
||||
var Feature_1 = require("../types/Feature");
|
||||
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||
var RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
||||
var relation_1 = require("oak-domain/lib/store/relation");
|
||||
var RelationAuth = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(RelationAuth, _super);
|
||||
function RelationAuth(aspectWrapper, cache, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities) {
|
||||
function RelationAuth(aspectWrapper, contextBuilder, cache, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.aspectWrapper = aspectWrapper,
|
||||
_this.cache = cache;
|
||||
_this.aspectWrapper = aspectWrapper;
|
||||
_this.contextBuilder = contextBuilder;
|
||||
_this.cache = cache;
|
||||
_this.actionCascadePathGraph = actionCascadePathGraph;
|
||||
_this.relationCascadePathGraph = relationCascadePathGraph;
|
||||
_this.actionCascadePathMap = {};
|
||||
|
|
@ -163,8 +165,21 @@ var RelationAuth = /** @class */ (function (_super) {
|
|||
var relationAuths = this.relationCascadePathGraph.filter(function (ele) { return ele[0] === entity && ir === ele[3]; });
|
||||
return relationAuths;
|
||||
};
|
||||
RelationAuth.prototype.checkRelation = function (entity, operation, context) {
|
||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
||||
RelationAuth.prototype.checkRelation = function (entity, operation) {
|
||||
var context = this.contextBuilder();
|
||||
context.begin();
|
||||
try {
|
||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
||||
}
|
||||
catch (err) {
|
||||
context.rollback();
|
||||
if (!(err instanceof types_1.OakUserException)) {
|
||||
throw err;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
context.rollback();
|
||||
return true;
|
||||
};
|
||||
RelationAuth.prototype.getRelationIdByName = function (entity, name, entityId) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ declare abstract class Node<ED extends EntityDict & BaseEntityDict, T extends ke
|
|||
getParent(): SingleNode<ED, keyof ED, Cxt, FrontCxt, AD> | ListNode<ED, T, Cxt, FrontCxt, AD> | VirtualNode<ED, Cxt, FrontCxt, AD> | undefined;
|
||||
protected getProjection(context?: FrontCxt): ED[T]['Selection']['data'] | undefined;
|
||||
setProjection(projection: ED[T]['Selection']['data']): void;
|
||||
protected judgeRelation(attr: string): string | 0 | 1 | 2 | string[];
|
||||
protected judgeRelation(attr: string): string | 0 | 1 | string[] | 2;
|
||||
protected contains(filter: ED[T]['Selection']['filter'], conditionalFilter: ED[T]['Selection']['filter']): boolean;
|
||||
protected repel(filter1: ED[T]['Selection']['filter'], filter2: ED[T]['Selection']['filter']): boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { InitializeOptions } from './types/Initialize';
|
|||
* @param actionDict
|
||||
* @returns
|
||||
*/
|
||||
export declare function initialize<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>>(storageSchema: StorageSchema<ED>, frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt, backendContextBuilder: (contextStr?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, aspectDict: AD, triggers: Array<Trigger<ED, keyof ED, Cxt>>, checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>, watchers: Array<Watcher<ED, keyof ED, Cxt>>, timers: Array<Timer<ED, Cxt>>, startRoutines: Array<Routine<ED, Cxt>>, initialData: {
|
||||
export declare function initialize<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>>(storageSchema: StorageSchema<ED>, frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt, backendContextBuilder: (contextStr?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>, aspectDict: AD, triggers: Array<Trigger<ED, keyof ED, Cxt | FrontCxt>>, checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>, watchers: Array<Watcher<ED, keyof ED, Cxt>>, timers: Array<Timer<ED, Cxt>>, startRoutines: Array<Routine<ED, Cxt>>, initialData: {
|
||||
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
|
||||
}, option: InitializeOptions<ED>): {
|
||||
features: import("./features").BasicFeatures<ED, Cxt, FrontCxt, CommonAspectDict<ED, Cxt> & AD>;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initialize = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var index_1 = require("oak-domain/lib/checkers/index");
|
||||
var index_2 = require("oak-domain/lib/triggers/index");
|
||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||
var debugStore_1 = require("./debugStore");
|
||||
var features_1 = require("./features");
|
||||
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||
var oak_common_aspect_1 = tslib_1.__importDefault(require("oak-common-aspect"));
|
||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||
var oak_common_aspect_2 = require("oak-common-aspect");
|
||||
var CacheStore_1 = require("./cacheStore/CacheStore");
|
||||
/**
|
||||
|
|
@ -32,9 +30,11 @@ function initialize(storageSchema, frontendContextBuilder, backendContextBuilder
|
|||
throw new Error("\u7528\u6237\u5B9A\u4E49\u7684aspect\u4E2D\u4E0D\u80FD\u548C\u7CFB\u7EDFaspect\u540C\u540D\uFF1A\u300C".concat(intersected.join(','), "\u300D"));
|
||||
}
|
||||
var aspectDict2 = Object.assign({}, aspectDict, oak_common_aspect_1.default);
|
||||
var checkers2 = (checkers).concat((0, index_1.createDynamicCheckers)(storageSchema));
|
||||
var triggers2 = (0, index_2.createDynamicTriggers)(storageSchema).concat(triggers);
|
||||
var debugStore = (0, debugStore_1.createDebugStore)(storageSchema, backendContextBuilder, triggers2, checkers2, watchers, timers, startRoutines, initialData, actionDict, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||
var _a = (0, actionDef_1.makeIntrinsicCTWs)(storageSchema, actionDict), intCheckers = _a.checkers, intTriggers = _a.triggers, intWatchers = _a.watchers;
|
||||
var checkers2 = checkers.concat(intCheckers);
|
||||
var triggers2 = triggers.concat(intTriggers);
|
||||
var watchers2 = watchers.concat(intWatchers);
|
||||
var debugStore = (0, debugStore_1.createDebugStore)(storageSchema, backendContextBuilder, triggers2, checkers2, watchers2, timers, startRoutines, initialData, actionDict, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||
var cacheStore = new CacheStore_1.CacheStore(storageSchema, function () { return debugStore.getCurrentData(); }, function () { return (0, debugStore_1.clearMaterializedData)(); });
|
||||
var wrapper = {
|
||||
exec: function (name, params) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
|
|
@ -77,11 +77,6 @@ function initialize(storageSchema, frontendContextBuilder, backendContextBuilder
|
|||
};
|
||||
var features = (0, features_1.initialize)(wrapper, storageSchema, function () { return frontendContextBuilder()(cacheStore); }, cacheStore, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, colorDict);
|
||||
checkers2.forEach(function (checker) { return cacheStore.registerChecker(checker); });
|
||||
if (actionDict) {
|
||||
var adCheckers = (0, actionDef_1.analyzeActionDefDict)(storageSchema, actionDict).checkers;
|
||||
adCheckers.forEach(function (checker) { return cacheStore.registerChecker(checker); });
|
||||
}
|
||||
cacheStore.registerGeneralChecker('relation', function (entity, operation, context) { return features.relationAuth.checkRelation(entity, operation, context); });
|
||||
(0, oak_common_aspect_2.registerPorts)(importations || [], exportations || []);
|
||||
return {
|
||||
features: features,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ var tslib_1 = require("tslib");
|
|||
var features_1 = require("./features");
|
||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||
var CacheStore_1 = require("./cacheStore/CacheStore");
|
||||
var checkers_1 = require("oak-domain/lib/checkers");
|
||||
/**
|
||||
* @param storageSchema
|
||||
* @param createFeatures
|
||||
|
|
@ -22,7 +21,8 @@ var checkers_1 = require("oak-domain/lib/checkers");
|
|||
function initialize(storageSchema, frontendContextBuilder, connector, checkers, option) {
|
||||
var _this = this;
|
||||
var actionCascadePathGraph = option.actionCascadePathGraph, relationCascadePathGraph = option.relationCascadePathGraph, authDeduceRelationMap = option.authDeduceRelationMap, actionDict = option.actionDict, selectFreeEntities = option.selectFreeEntities, colorDict = option.colorDict;
|
||||
var checkers2 = (checkers || []).concat((0, checkers_1.createDynamicCheckers)(storageSchema));
|
||||
var intCheckers = (0, actionDef_1.makeIntrinsicCTWs)(storageSchema, actionDict).checkers;
|
||||
var checkers2 = checkers.concat(intCheckers);
|
||||
var cacheStore = new CacheStore_1.CacheStore(storageSchema);
|
||||
var wrapper = {
|
||||
exec: function (name, params) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
|
|
@ -45,11 +45,6 @@ function initialize(storageSchema, frontendContextBuilder, connector, checkers,
|
|||
};
|
||||
var features = (0, features_1.initialize)(wrapper, storageSchema, function () { return frontendContextBuilder()(cacheStore); }, cacheStore, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, colorDict, function (url, headers) { return connector.makeBridgeUrl(url, headers); });
|
||||
checkers2.forEach(function (checker) { return cacheStore.registerChecker(checker); });
|
||||
if (actionDict) {
|
||||
var adCheckers = (0, actionDef_1.analyzeActionDefDict)(storageSchema, actionDict).checkers;
|
||||
adCheckers.forEach(function (checker) { return cacheStore.registerChecker(checker); });
|
||||
}
|
||||
cacheStore.registerGeneralChecker('relation', function (entity, operation, context) { return features.relationAuth.checkRelation(entity, operation, context); });
|
||||
return {
|
||||
features: features,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ var lodash_1 = require("oak-domain/lib/utils/lodash");
|
|||
var relation_1 = require("oak-domain/lib/store/relation");
|
||||
var filter_1 = require("oak-domain/lib/store/filter");
|
||||
var runningTree_1 = require("./features/runningTree");
|
||||
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
function onPathSet(option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var _a, props, state, _b, oakPath, oakProjection, oakFilters, oakSorters, oakId, entity, path, projection, isList, filters, sorters, pagination, features, oakPath2, entity2_1, filters2, oakFilters2, _loop_1, filters_1, filters_1_1, ele, proj, sorters2, oakSorters2, _loop_2, sorters_1, sorters_1_1, ele, actions_1, cascadeActions_1;
|
||||
|
|
@ -172,7 +173,7 @@ function checkActionsAndCascadeEntities(rows, option) {
|
|||
var filter = this_1.features.runningTree.getIntrinsticFilters(this_1.state.oakFullpath);
|
||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||
// 创建对象的判定不落在具体行上,但要考虑list上外键相关属性的限制
|
||||
var data = typeof action === 'object' && (0, lodash_1.cloneDeep)(action.data);
|
||||
var data = typeof action === 'object' && Object.assign((0, lodash_1.cloneDeep)(action.data) || {}, { id: (0, uuid_1.generateNewId)() });
|
||||
if (this_1.checkOperation(this_1.state.oakEntity, 'create', data, filter, checkTypes)) {
|
||||
legalActions.push(action);
|
||||
}
|
||||
|
|
@ -295,9 +296,11 @@ function checkActionsAndCascadeEntities(rows, option) {
|
|||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||
rows.forEach(function (row) {
|
||||
var _a;
|
||||
var intrinsticData = rel_1[1] ? (_a = {},
|
||||
var intrinsticData = rel_1[1] ? (_a = {
|
||||
id: (0, uuid_1.generateNewId)()
|
||||
},
|
||||
_a[rel_1[1]] = row.id,
|
||||
_a) : { entity: _this.state.oakEntity, entityId: row.id };
|
||||
_a) : { id: (0, uuid_1.generateNewId)(), entity: _this.state.oakEntity, entityId: row.id };
|
||||
if (typeof action === 'object') {
|
||||
Object.assign(intrinsticData, action.data);
|
||||
}
|
||||
|
|
@ -339,9 +342,11 @@ function checkActionsAndCascadeEntities(rows, option) {
|
|||
}
|
||||
else {
|
||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||
var intrinsticData = rel_1[1] ? (_k = {},
|
||||
var intrinsticData = rel_1[1] ? (_k = {
|
||||
id: (0, uuid_1.generateNewId)()
|
||||
},
|
||||
_k[rel_1[1]] = rows.id,
|
||||
_k) : { entity: this_2.state.oakEntity, entityId: rows.id };
|
||||
_k) : { id: (0, uuid_1.generateNewId)(), entity: this_2.state.oakEntity, entityId: rows.id };
|
||||
if (typeof action === 'object') {
|
||||
Object.assign(intrinsticData, action.data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,13 @@ var oakBehavior = Behavior({
|
|||
return this.features.runningTree.getFreshValue(path2);
|
||||
},
|
||||
checkOperation: function (entity, action, data, filter, checkerTypes) {
|
||||
if (checkerTypes === null || checkerTypes === void 0 ? void 0 : checkerTypes.includes('relation')) {
|
||||
return this.features.relationAuth.checkRelation(entity, {
|
||||
action: action,
|
||||
data: data,
|
||||
filter: filter,
|
||||
}) && this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||
}
|
||||
return this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||
},
|
||||
tryExecute: function (path) {
|
||||
|
|
|
|||
|
|
@ -210,6 +210,13 @@ var OakComponentBase = /** @class */ (function (_super) {
|
|||
return this.features.runningTree.getFreshValue(path2);
|
||||
};
|
||||
OakComponentBase.prototype.checkOperation = function (entity, action, data, filter, checkerTypes) {
|
||||
if (checkerTypes === null || checkerTypes === void 0 ? void 0 : checkerTypes.includes('relation')) {
|
||||
return this.features.relationAuth.checkRelation(entity, {
|
||||
action: action,
|
||||
data: data,
|
||||
filter: filter,
|
||||
}) && this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||
}
|
||||
return this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||
};
|
||||
OakComponentBase.prototype.tryExecute = function (path) {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
import { AggregationResult, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { StorageSchema } from "oak-domain/lib/types/Storage";
|
||||
import { readOnlyActions } from 'oak-domain/lib/actions/action';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, TxnOption } from 'oak-domain/lib/types';
|
||||
import { Checker, CheckerType, Trigger, TxnOption } from 'oak-domain/lib/types';
|
||||
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||
import assert from 'assert';
|
||||
import { SyncContext, SyncRowStore } from 'oak-domain/lib/store/SyncRowStore';
|
||||
import CheckerExecutor from './CheckerExecutor';
|
||||
import SyncTriggerExecutor from './SyncTriggerExecutor';
|
||||
|
||||
interface CachStoreOperation extends TreeStoreOperateOption {};
|
||||
interface CachStoreOperation extends TreeStoreOperateOption {
|
||||
checkerTypes?: CheckerType[];
|
||||
};
|
||||
|
||||
export class CacheStore<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
Cxt extends SyncContext<ED>
|
||||
> extends TreeStore<ED> implements SyncRowStore<ED, SyncContext<ED>>{
|
||||
private checkerExecutor: CheckerExecutor<ED, Cxt>;
|
||||
private triggerExecutor: SyncTriggerExecutor<ED, Cxt>;
|
||||
private getFullDataFn?: () => any;
|
||||
private resetInitialDataFn?: () => void;
|
||||
|
||||
|
|
@ -23,7 +26,7 @@ export class CacheStore<
|
|||
resetInitialDataFn?: () => void
|
||||
) {
|
||||
super(storageSchema);
|
||||
this.checkerExecutor = new CheckerExecutor();
|
||||
this.triggerExecutor = new SyncTriggerExecutor();
|
||||
this.getFullDataFn = getFullDataFn;
|
||||
this.resetInitialDataFn = resetInitialDataFn;
|
||||
}
|
||||
|
|
@ -32,21 +35,24 @@ export class CacheStore<
|
|||
return this.aggregateSync(entity, aggregation, context, option);
|
||||
}
|
||||
|
||||
protected cascadeUpdate<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: SyncContext<ED>, option: OP): OperationResult<ED> {
|
||||
protected cascadeUpdate<T extends keyof ED, OP extends CachStoreOperation>(entity: T, operation: ED[T]['Operation'], context: SyncContext<ED>, option: OP): OperationResult<ED> {
|
||||
assert(context.getCurrentTxnId());
|
||||
if (!option.blockTrigger) {
|
||||
this.checkerExecutor.check(entity, operation, context as Cxt, 'before');
|
||||
this.triggerExecutor.check(entity, operation, context as Cxt, 'before', option.checkerTypes);
|
||||
}
|
||||
const result = super.cascadeUpdate(entity, operation, context, option);
|
||||
if (operation.data) {
|
||||
const result = super.cascadeUpdate(entity, operation, context, option);
|
||||
|
||||
if (!option.blockTrigger) {
|
||||
this.triggerExecutor.check(entity, operation, context as Cxt, 'after', option.checkerTypes);
|
||||
}
|
||||
|
||||
if (!option.blockTrigger) {
|
||||
this.checkerExecutor.check(entity, operation, context as Cxt, 'after');
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
operate<T extends keyof ED, OP extends TreeStoreOperateOption>(
|
||||
operate<T extends keyof ED, OP extends CachStoreOperation>(
|
||||
entity: T,
|
||||
operation: ED[T]['Operation'],
|
||||
context: Cxt,
|
||||
|
|
@ -65,7 +71,7 @@ export class CacheStore<
|
|||
let result;
|
||||
|
||||
try {
|
||||
result = super.sync<CachStoreOperation, Cxt>(opRecords, context, {});
|
||||
result = super.sync<OperateOption, Cxt>(opRecords, context, {});
|
||||
} catch (err) {
|
||||
if (autoCommit) {
|
||||
context.rollback();
|
||||
|
|
@ -80,7 +86,15 @@ export class CacheStore<
|
|||
|
||||
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, checkerTypes?: CheckerType[]) {
|
||||
assert(context.getCurrentTxnId());
|
||||
this.checkerExecutor.check(entity, operation, context, undefined, checkerTypes);
|
||||
const { action } = operation;
|
||||
if (readOnlyActions.includes(action)) {
|
||||
// 只读查询的checker只能在根部入口执行
|
||||
this.triggerExecutor.check(entity, operation, context, undefined, checkerTypes);
|
||||
return;
|
||||
}
|
||||
this.operate(entity, operation as ED[T]['Operation'], context, {
|
||||
checkerTypes,
|
||||
});
|
||||
}
|
||||
|
||||
select<
|
||||
|
|
@ -109,12 +123,12 @@ export class CacheStore<
|
|||
}
|
||||
|
||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
|
||||
this.checkerExecutor.registerChecker(checker);
|
||||
this.triggerExecutor.registerChecker(checker);
|
||||
}
|
||||
|
||||
registerGeneralChecker(type: CheckerType, fn: <T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt) => void) {
|
||||
this.checkerExecutor.registerGeneralChecker(type, fn);
|
||||
}
|
||||
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||
this.triggerExecutor.registerTrigger(trigger);
|
||||
} */
|
||||
|
||||
/**
|
||||
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
// 简化版的对checker的同步检查
|
||||
import assert from 'assert';
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, SelectOption, OperateOption, DATA_CHECKER_DEFAULT_PRIORITY, CHECKER_DEFAULT_PRIORITY } from 'oak-domain/lib/types';
|
||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
import { translateCheckerInSyncContext } from 'oak-domain/lib/store/checker';
|
||||
import { checkFilterRepel } from 'oak-domain/lib/store/filter';
|
||||
|
||||
export default class CheckerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends SyncContext<ED>> {
|
||||
static All_Checker_Types: CheckerType[] = ['data', 'logical', 'logicalRelation', 'relation', 'row'];
|
||||
private checkerMap: {
|
||||
[K in keyof ED]?: {
|
||||
[A: string]: Array<{
|
||||
priority: number;
|
||||
fn: (operation: Omit<ED[K]['Operation'], 'id'>, context: SyncContext<ED>, option: SelectOption | OperateOption) => void;
|
||||
type: CheckerType;
|
||||
when: 'before' | 'after';
|
||||
filter?: ED[K]['Update']['filter'] | ((operation: Omit<ED[K]['Operation'], 'id'>, context: SyncContext<ED>, option: SelectOption | OperateOption) => ED[K]['Update']['filter']);
|
||||
}>;
|
||||
};
|
||||
} = {};
|
||||
private generalCheckerMap: {
|
||||
[K in CheckerType]?: Array<<T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt) => void>
|
||||
} = {};
|
||||
|
||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
|
||||
let { entity, action, priority, type, conditionalFilter } = checker;
|
||||
const { fn, when } = translateCheckerInSyncContext(checker);
|
||||
if (priority === undefined) {
|
||||
priority = type === 'data' ? DATA_CHECKER_DEFAULT_PRIORITY : CHECKER_DEFAULT_PRIORITY;
|
||||
}
|
||||
const addCheckerMap = (action2: string) => {
|
||||
if (this.checkerMap[entity] && this.checkerMap[entity]![action2]) {
|
||||
let iter = 0;
|
||||
const checkers = this.checkerMap[entity]![action2]!;
|
||||
for (; iter < checkers.length; iter ++) {
|
||||
if (priority! <= checkers[iter].priority!) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkers.splice(iter, 0, {
|
||||
type,
|
||||
priority: priority!,
|
||||
fn: fn as (operation: Omit<ED[T]['Operation'], 'id'>, context: SyncContext<ED>, option: OperateOption | SelectOption) => void,
|
||||
when,
|
||||
filter: conditionalFilter,
|
||||
});
|
||||
}
|
||||
else if (this.checkerMap[entity]) {
|
||||
Object.assign(this.checkerMap[entity]!, {
|
||||
[action2]: [{
|
||||
type,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter: conditionalFilter,
|
||||
}],
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.assign(this.checkerMap, {
|
||||
[entity]: {
|
||||
[action2]: [{
|
||||
type,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter: conditionalFilter,
|
||||
}],
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
if (action instanceof Array) {
|
||||
action.forEach(
|
||||
a => addCheckerMap(a as string)
|
||||
);
|
||||
}
|
||||
else {
|
||||
addCheckerMap(action as string);
|
||||
}
|
||||
}
|
||||
|
||||
registerGeneralChecker(type: CheckerType, fn: <T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt) => void) {
|
||||
if (this.generalCheckerMap[type]) {
|
||||
this.generalCheckerMap[type]?.push(fn);
|
||||
}
|
||||
else {
|
||||
this.generalCheckerMap[type] = [fn];
|
||||
}
|
||||
}
|
||||
|
||||
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, when?: 'before' | 'after', checkerTypes?: CheckerType[]) {
|
||||
const { action } = operation;
|
||||
const checkers = this.checkerMap[entity] && this.checkerMap[entity]![action];
|
||||
if (checkers) {
|
||||
const checkers2 = checkers.filter(ele => (!checkerTypes || checkerTypes.includes(ele.type)) && (!when || ele.when === when));
|
||||
for (const checker of checkers2) {
|
||||
const { filter } = checker;
|
||||
if (filter) {
|
||||
const filterr = typeof filter === 'function' ? filter(operation, context, {}) : filter;
|
||||
assert(!(filter instanceof Promise), `对${entity as string}的动作${action}上定义的checker,其filter返回了Promise,请注意将同步和异步的返回区分对待`);
|
||||
const isRepel = checkFilterRepel<ED, T, SyncContext<ED>>(entity, context, filterr, operation.filter, true);
|
||||
assert(typeof isRepel === 'boolean');
|
||||
if (isRepel) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
checker.fn(operation, context, {} as any);
|
||||
}
|
||||
}
|
||||
|
||||
const types = checkerTypes || CheckerExecutor.All_Checker_Types;
|
||||
types.forEach(
|
||||
(type) => {
|
||||
if (this.generalCheckerMap[type]) {
|
||||
this.generalCheckerMap[type]?.forEach(
|
||||
(fn) => fn(entity, operation, context)
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
// 简化版的对checker的同步检查
|
||||
import assert from 'assert';
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, SelectOption, OperateOption, CHECKER_PRIORITY_MAP,
|
||||
Trigger, RemoveTrigger, UpdateTrigger, TRIGGER_DEFAULT_PRIORITY } from 'oak-domain/lib/types';
|
||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
import { translateCheckerInSyncContext } from 'oak-domain/lib/store/checker';
|
||||
import { checkFilterRepel } from 'oak-domain/lib/store/filter';
|
||||
|
||||
export default class SyncTriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends SyncContext<ED>> {
|
||||
static All_Checker_Types: CheckerType[] = ['data', 'logical', 'logicalRelation', 'relation', 'row'];
|
||||
private checkerMap: {
|
||||
[K in keyof ED]?: {
|
||||
[A: string]: Array<{
|
||||
priority: number;
|
||||
fn: (operation: ED[K]['Operation'], context: SyncContext<ED>, option: SelectOption | OperateOption) => void;
|
||||
type: CheckerType;
|
||||
when: 'before' | 'after';
|
||||
filter?: ED[K]['Update']['filter'] | ((operation: ED[K]['Operation'], context: SyncContext<ED>, option: SelectOption | OperateOption) => ED[K]['Update']['filter']);
|
||||
}>;
|
||||
};
|
||||
} = {};
|
||||
|
||||
private addToCheckerMap<T extends keyof ED>(action: string, entity: T, priority: number, when: 'before' | 'after',
|
||||
fn: (operation: ED[T]['Operation'], context: SyncContext<ED>, option: SelectOption | OperateOption) => void,
|
||||
type: CheckerType,
|
||||
filter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: SyncContext<ED>, option: SelectOption | OperateOption) => ED[T]['Update']['filter'])) {
|
||||
if (this.checkerMap[entity] && this.checkerMap[entity]![action]) {
|
||||
let iter = 0;
|
||||
const checkers = this.checkerMap[entity]![action]!;
|
||||
for (; iter < checkers.length; iter++) {
|
||||
if (priority! <= checkers[iter].priority!) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkers.splice(iter, 0, {
|
||||
type,
|
||||
priority: priority!,
|
||||
fn,
|
||||
when,
|
||||
filter,
|
||||
});
|
||||
}
|
||||
else if (this.checkerMap[entity]) {
|
||||
Object.assign(this.checkerMap[entity]!, {
|
||||
[action]: [{
|
||||
type,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter,
|
||||
}],
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.assign(this.checkerMap, {
|
||||
[entity]: {
|
||||
[action]: [{
|
||||
type,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter,
|
||||
}],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
|
||||
let { entity, action, priority, type, conditionalFilter } = checker;
|
||||
const { fn, when } = translateCheckerInSyncContext(checker);
|
||||
if (action instanceof Array) {
|
||||
action.forEach(
|
||||
a => this.addToCheckerMap(a as string, entity, priority || CHECKER_PRIORITY_MAP[type], when, fn as any, type, conditionalFilter)
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.addToCheckerMap(action as string, entity, priority!, when, fn as any, type, conditionalFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||
const {
|
||||
action,
|
||||
entity,
|
||||
priority,
|
||||
fn,
|
||||
when,
|
||||
filter,
|
||||
} = trigger as UpdateTrigger<ED, T, Cxt>;
|
||||
|
||||
if (when === 'commit') {
|
||||
return;
|
||||
}
|
||||
if (action instanceof Array) {
|
||||
action.forEach(
|
||||
(a) => this.addToCheckerMap(a, entity, priority || TRIGGER_DEFAULT_PRIORITY, when, fn as any, undefined, filter)
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.addToCheckerMap(action ,entity, priority || TRIGGER_DEFAULT_PRIORITY, when, fn as any, undefined, filter);
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, when?: 'before' | 'after', checkerTypes?: CheckerType[]) {
|
||||
const { action } = operation;
|
||||
const checkers = this.checkerMap[entity] && this.checkerMap[entity]![action];
|
||||
if (checkers) {
|
||||
const checkers2 = checkers.filter(ele => (!checkerTypes || checkerTypes.includes(ele.type)) && (!when || ele.when === when));
|
||||
for (const checker of checkers2) {
|
||||
const { filter } = checker;
|
||||
if (filter) {
|
||||
const filterr = typeof filter === 'function' ? filter(operation as ED[T]['Operation'], context, {}) : filter;
|
||||
assert(!(filter instanceof Promise), `对${entity as string}的动作${action}上定义的checker,其filter返回了Promise,请注意将同步和异步的返回区分对待`);
|
||||
const isRepel = checkFilterRepel<ED, T, SyncContext<ED>>(entity, context, filterr, operation.filter, true);
|
||||
assert(typeof isRepel === 'boolean');
|
||||
if (isRepel) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
checker.fn(operation as ED[T]['Operation'], context, {} as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import { DebugStore } from './DebugStore';
|
|||
import {
|
||||
Checker, Trigger, StorageSchema, EntityDict, ActionDictOfEntityDict, Watcher, BBWatcher, WBWatcher, Routine, Timer, AuthCascadePath, AuthDeduceRelationMap} from "oak-domain/lib/types";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { analyzeActionDefDict } from 'oak-domain/lib/store/actionDef';
|
||||
|
||||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
|
|
@ -268,13 +268,6 @@ export function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt ext
|
|||
);
|
||||
|
||||
assert(actionDict);
|
||||
const { triggers: adTriggers, checkers: adCheckers, watchers: adWatchers } = analyzeActionDefDict(storageSchema, actionDict!);
|
||||
adTriggers.forEach(
|
||||
ele => store.registerTrigger(ele)
|
||||
);
|
||||
adCheckers.forEach(
|
||||
ele => store.registerChecker(ele)
|
||||
);
|
||||
|
||||
// 如果没有物化数据则使用initialData初始化debugStore
|
||||
const data = getMaterializedData();
|
||||
|
|
@ -299,7 +292,7 @@ export function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt ext
|
|||
}, 10000);
|
||||
|
||||
// 启动watcher
|
||||
initializeWatchers(store, contextBuilder, watchers.concat(adWatchers));
|
||||
initializeWatchers(store, contextBuilder, watchers);
|
||||
// 启动timer
|
||||
if (timers) {
|
||||
initializeTimers(store, contextBuilder, timers);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { OakRowUnexistedException, OakRowInconsistencyException, OakException, O
|
|||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
import assert from 'assert';
|
||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
|
||||
export class Cache<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
|
|
@ -210,7 +211,7 @@ export class Cache<
|
|||
const operation = {
|
||||
action,
|
||||
filter,
|
||||
data,
|
||||
data
|
||||
} as ED[T]['Update'];
|
||||
try {
|
||||
this.cacheStore!.check(entity, operation, context, checkerTypes);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ 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,6 +32,7 @@ export class ContextMenuFactory<
|
|||
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||
menuWrappers?: IMenuWrapper<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(
|
||||
|
|
@ -112,10 +115,11 @@ export class ContextMenuFactory<
|
|||
);
|
||||
}
|
||||
|
||||
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]) {
|
||||
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]) {
|
||||
super();
|
||||
this.cache = cache;
|
||||
this.cascadePathGraph = cascadePathGraph;
|
||||
this.relationAuth = relationAuth;
|
||||
}
|
||||
|
||||
setMenus(menus: IMenu<ED, keyof ED>[]) {
|
||||
|
|
@ -132,7 +136,14 @@ export class ContextMenuFactory<
|
|||
if (filters.length > 0) {
|
||||
// 这里应该是or关系,paths表达的路径中只要有一条满足就可能满足
|
||||
const allows = filters.map(
|
||||
(filter) => this.cache.checkOperation(destEntity, action, undefined, filter, ['logical', 'relation', 'logicalRelation', 'row'])
|
||||
(filter) => {
|
||||
// relationAuth和其它的checker现在分开判断
|
||||
return this.relationAuth.checkRelation(destEntity, {
|
||||
action,
|
||||
data: undefined as any,
|
||||
filter,
|
||||
} as Omit<ED[keyof ED]['Operation'], 'id'>) && this.cache.checkOperation(destEntity, action, undefined, filter, ['logical', 'relation', 'logicalRelation', 'row']);
|
||||
}
|
||||
);
|
||||
if (allows.indexOf(true) >= 0) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export function initialize<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
|||
makeBridgeUrlFn?: (url: string, headers?: Record<string, string>) => string) {
|
||||
const cache = new Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper, contextBuilder, store);
|
||||
const location = new Location();
|
||||
const relationAuth = new RelationAuth<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper, cache,
|
||||
const relationAuth = new RelationAuth<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper, contextBuilder, cache,
|
||||
actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||
const runningTree = new RunningTree<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(cache, storageSchema, relationAuth);
|
||||
const locales = new Locales(aspectWrapper, makeBridgeUrlFn);
|
||||
|
|
@ -47,7 +47,7 @@ export function initialize<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
|||
const navigator = new Navigator();
|
||||
const port = new Port<ED, Cxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper);
|
||||
const style = new Style<ED>(colorDict);
|
||||
const contextMenuFactory = new ContextMenuFactory<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(cache, actionCascadePathGraph);
|
||||
const contextMenuFactory = new ContextMenuFactory<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(cache, relationAuth, actionCascadePathGraph);
|
||||
return {
|
||||
cache,
|
||||
location,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict, OperateOption, SelectOption, OpRecord, AspectWrapper, CheckerType, Aspect, SelectOpResult, AuthCascadePath, AuthDeduceRelationMap } from 'oak-domain/lib/types';
|
||||
import { EntityDict, OperateOption, SelectOption, OpRecord, AspectWrapper, CheckerType, Aspect, SelectOpResult, AuthCascadePath, AuthDeduceRelationMap, OakUserException } from 'oak-domain/lib/types';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { CommonAspectDict } from 'oak-common-aspect';
|
||||
import { Feature } from '../types/Feature';
|
||||
|
|
@ -17,6 +17,7 @@ export class RelationAuth<
|
|||
AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>
|
||||
> extends Feature {
|
||||
private cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||
private contextBuilder: () => FrontCxt;
|
||||
private aspectWrapper: AspectWrapper<ED, Cxt, AD>;
|
||||
private actionCascadePathGraph: AuthCascadePath<ED>[];
|
||||
private actionCascadePathMap: Record<string, AuthCascadePath<ED>[]>;
|
||||
|
|
@ -35,6 +36,7 @@ export class RelationAuth<
|
|||
|
||||
constructor(
|
||||
aspectWrapper: AspectWrapper<ED, Cxt, AD>,
|
||||
contextBuilder: () => FrontCxt,
|
||||
cache: Cache<ED, Cxt, FrontCxt, AD>,
|
||||
actionCascadePathGraph: AuthCascadePath<ED>[],
|
||||
relationCascadePathGraph: AuthCascadePath<ED>[],
|
||||
|
|
@ -42,7 +44,8 @@ export class RelationAuth<
|
|||
selectFreeEntities: (keyof ED)[],
|
||||
) {
|
||||
super();
|
||||
this.aspectWrapper = aspectWrapper,
|
||||
this.aspectWrapper = aspectWrapper;
|
||||
this.contextBuilder = contextBuilder;
|
||||
this.cache = cache;
|
||||
this.actionCascadePathGraph = actionCascadePathGraph;
|
||||
this.relationCascadePathGraph = relationCascadePathGraph;
|
||||
|
|
@ -223,8 +226,21 @@ export class RelationAuth<
|
|||
return relationAuths;
|
||||
}
|
||||
|
||||
checkRelation<T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: FrontCxt) {
|
||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
||||
checkRelation<T extends keyof ED>(entity: T, operation: Omit< ED[T]['Operation'] | ED[T]['Selection'], 'id'>) {
|
||||
const context = this.contextBuilder();
|
||||
context.begin();
|
||||
try {
|
||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
||||
}
|
||||
catch (err) {
|
||||
context.rollback();
|
||||
if (!(err instanceof OakUserException)) {
|
||||
throw err;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
context.rollback();
|
||||
return true;
|
||||
}
|
||||
|
||||
async getRelationIdByName(entity: keyof ED, name: string, entityId?: string) {
|
||||
|
|
|
|||
|
|
@ -10,15 +10,13 @@ import {
|
|||
} from 'oak-domain/lib/types';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { createDynamicCheckers } from 'oak-domain/lib/checkers/index';
|
||||
import { createDynamicTriggers } from 'oak-domain/lib/triggers/index';
|
||||
import { makeIntrinsicCTWs } from 'oak-domain/lib/store/actionDef';
|
||||
|
||||
import { createDebugStore, clearMaterializedData } from './debugStore';
|
||||
|
||||
import { initialize as initBasicFeatures } from './features';
|
||||
import { intersection } from 'oak-domain/lib/utils/lodash';
|
||||
import commonAspectDict from 'oak-common-aspect';
|
||||
import { analyzeActionDefDict } from 'oak-domain/lib/store/actionDef';
|
||||
import { CommonAspectDict, registerPorts } from 'oak-common-aspect';
|
||||
import { CacheStore } from './cacheStore/CacheStore';
|
||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||
|
|
@ -49,7 +47,7 @@ export function initialize<
|
|||
frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt,
|
||||
backendContextBuilder: (contextStr?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>,
|
||||
aspectDict: AD,
|
||||
triggers: Array<Trigger<ED, keyof ED, Cxt>>,
|
||||
triggers: Array<Trigger<ED, keyof ED, Cxt | FrontCxt>>,
|
||||
checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>,
|
||||
watchers: Array<Watcher<ED, keyof ED, Cxt>>,
|
||||
timers: Array<Timer<ED, Cxt>>,
|
||||
|
|
@ -68,14 +66,16 @@ export function initialize<
|
|||
);
|
||||
}
|
||||
const aspectDict2 = Object.assign({}, aspectDict, commonAspectDict);
|
||||
const checkers2 = (checkers).concat(createDynamicCheckers<ED, Cxt | FrontCxt>(storageSchema));
|
||||
const triggers2 = createDynamicTriggers<ED, Cxt>(storageSchema).concat(triggers);
|
||||
const { checkers: intCheckers, triggers: intTriggers, watchers: intWatchers } = makeIntrinsicCTWs<ED, Cxt, FrontCxt>(storageSchema, actionDict);
|
||||
const checkers2 = checkers.concat(intCheckers);
|
||||
const triggers2 = triggers.concat(intTriggers);
|
||||
const watchers2 = watchers.concat(intWatchers);
|
||||
const debugStore = createDebugStore(
|
||||
storageSchema,
|
||||
backendContextBuilder,
|
||||
triggers2,
|
||||
checkers2,
|
||||
watchers,
|
||||
watchers2,
|
||||
timers,
|
||||
startRoutines,
|
||||
initialData,
|
||||
|
|
@ -125,15 +125,7 @@ export function initialize<
|
|||
selectFreeEntities,
|
||||
colorDict);
|
||||
|
||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker as Checker<ED, keyof ED, SyncContext<ED>>));
|
||||
if (actionDict) {
|
||||
const { checkers: adCheckers } = analyzeActionDefDict(
|
||||
storageSchema,
|
||||
actionDict
|
||||
);
|
||||
adCheckers.forEach((checker) => cacheStore.registerChecker(checker));
|
||||
}
|
||||
cacheStore.registerGeneralChecker('relation', (entity, operation, context) => features.relationAuth.checkRelation(entity, operation, context));
|
||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker));
|
||||
|
||||
registerPorts(importations || [], exportations || []);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import {
|
|||
Checker,
|
||||
StorageSchema,
|
||||
Connector,
|
||||
Trigger,
|
||||
} from 'oak-domain/lib/types';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
|
||||
import { initialize as initBasicFeatures } from './features';
|
||||
import { analyzeActionDefDict } from 'oak-domain/lib/store/actionDef';
|
||||
import { makeIntrinsicCTWs } from 'oak-domain/lib/store/actionDef';
|
||||
import { CommonAspectDict } from 'oak-common-aspect';
|
||||
import { CacheStore } from './cacheStore/CacheStore';
|
||||
import { createDynamicCheckers } from 'oak-domain/lib/checkers';
|
||||
|
|
@ -43,7 +44,10 @@ export function initialize<
|
|||
option: InitializeOptions<ED>
|
||||
) {
|
||||
const { actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, actionDict, selectFreeEntities, colorDict } = option;
|
||||
const checkers2 = (checkers || []).concat(createDynamicCheckers<ED, Cxt | FrontCxt>(storageSchema));
|
||||
|
||||
|
||||
const { checkers: intCheckers } = makeIntrinsicCTWs<ED, Cxt, FrontCxt>(storageSchema, actionDict);
|
||||
const checkers2 = checkers.concat(intCheckers);
|
||||
|
||||
const cacheStore = new CacheStore<ED, FrontCxt>(
|
||||
storageSchema,
|
||||
|
|
@ -74,15 +78,7 @@ export function initialize<
|
|||
(url, headers) => connector.makeBridgeUrl(url, headers)
|
||||
);
|
||||
|
||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker as Checker<ED, keyof ED, SyncContext<ED>>));
|
||||
if (actionDict) {
|
||||
const { checkers: adCheckers } = analyzeActionDefDict(
|
||||
storageSchema,
|
||||
actionDict
|
||||
);
|
||||
adCheckers.forEach((checker) => cacheStore.registerChecker(checker));
|
||||
}
|
||||
cacheStore.registerGeneralChecker('relation', (entity, operation, context) => features.relationAuth.checkRelation(entity, operation, context));
|
||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker));
|
||||
|
||||
return {
|
||||
features,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { MessageProps } from './types/Message';
|
|||
import { judgeRelation } from 'oak-domain/lib/store/relation';
|
||||
import { addFilterSegment, combineFilters } from 'oak-domain/lib/store/filter';
|
||||
import { MODI_NEXT_PATH_SUFFIX } from './features/runningTree';
|
||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
|
||||
export async function onPathSet<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
|
|
@ -174,7 +175,7 @@ function checkActionsAndCascadeEntities<
|
|||
const filter = this.features.runningTree.getIntrinsticFilters(this.state.oakFullpath!);
|
||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||
// 创建对象的判定不落在具体行上,但要考虑list上外键相关属性的限制
|
||||
const data = typeof action === 'object' && cloneDeep(action.data);
|
||||
const data = typeof action === 'object' && Object.assign(cloneDeep(action.data) || {}, { id: generateNewId() });
|
||||
if (this.checkOperation(this.state.oakEntity, 'create', data as any, filter, checkTypes)) {
|
||||
legalActions.push(action);
|
||||
}
|
||||
|
|
@ -289,8 +290,9 @@ function checkActionsAndCascadeEntities<
|
|||
rows.forEach(
|
||||
(row) => {
|
||||
const intrinsticData = rel[1] ? {
|
||||
id: generateNewId(),
|
||||
[rel[1]]: row.id,
|
||||
} : { entity: this.state.oakEntity, entityId: row.id };
|
||||
} : { id: generateNewId(), entity: this.state.oakEntity, entityId: row.id };
|
||||
if (typeof action === 'object') {
|
||||
Object.assign(intrinsticData, action.data);
|
||||
}
|
||||
|
|
@ -338,8 +340,9 @@ function checkActionsAndCascadeEntities<
|
|||
else {
|
||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||
const intrinsticData = rel[1] ? {
|
||||
id: generateNewId(),
|
||||
[rel[1]]: rows.id,
|
||||
} : { entity: this.state.oakEntity, entityId: rows.id };
|
||||
} : { id: generateNewId(), entity: this.state.oakEntity, entityId: rows.id };
|
||||
if (typeof action === 'object') {
|
||||
Object.assign(intrinsticData, action.data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,6 +320,22 @@ const oakBehavior = Behavior<
|
|||
},
|
||||
|
||||
checkOperation(entity, action, data, filter, checkerTypes) {
|
||||
if (checkerTypes?.includes('relation')) {
|
||||
return this.features.relationAuth.checkRelation(
|
||||
entity,
|
||||
{
|
||||
action,
|
||||
data,
|
||||
filter,
|
||||
} as Omit<EDD[keyof EDD]['Operation'], 'id'>
|
||||
) && this.features.cache.checkOperation(
|
||||
entity,
|
||||
action,
|
||||
data,
|
||||
filter,
|
||||
checkerTypes
|
||||
)
|
||||
}
|
||||
return this.features.cache.checkOperation(
|
||||
entity,
|
||||
action,
|
||||
|
|
|
|||
|
|
@ -388,6 +388,22 @@ abstract class OakComponentBase<
|
|||
filter?: ED[T]['Update']['filter'],
|
||||
checkerTypes?: CheckerType[]
|
||||
) {
|
||||
if (checkerTypes?.includes('relation')) {
|
||||
return this.features.relationAuth.checkRelation(
|
||||
entity,
|
||||
{
|
||||
action,
|
||||
data,
|
||||
filter,
|
||||
} as Omit<ED[keyof ED]['Operation'], 'id'>
|
||||
) && this.features.cache.checkOperation(
|
||||
entity,
|
||||
action,
|
||||
data,
|
||||
filter,
|
||||
checkerTypes
|
||||
)
|
||||
}
|
||||
return this.features.cache.checkOperation(
|
||||
entity,
|
||||
action,
|
||||
|
|
|
|||
Loading…
Reference in New Issue