适配了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 { StorageSchema } from "oak-domain/lib/types/Storage";
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { Checker, CheckerType, TxnOption } from 'oak-domain/lib/types';
|
import { Checker, CheckerType, TxnOption } from 'oak-domain/lib/types';
|
||||||
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||||
import { SyncContext, SyncRowStore } from 'oak-domain/lib/store/SyncRowStore';
|
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>> {
|
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 getFullDataFn?;
|
||||||
private resetInitialDataFn?;
|
private resetInitialDataFn?;
|
||||||
constructor(storageSchema: StorageSchema<ED>, getFullDataFn?: () => any, resetInitialDataFn?: () => void);
|
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']>;
|
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>;
|
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 TreeStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, 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;
|
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;
|
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"]>[];
|
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;
|
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下不能使用
|
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
||||||
* @returns
|
* @returns
|
||||||
|
|
@ -32,3 +34,4 @@ export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt exte
|
||||||
commit(txnId: string): void;
|
commit(txnId: string): void;
|
||||||
rollback(txnId: string): void;
|
rollback(txnId: string): void;
|
||||||
}
|
}
|
||||||
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.CacheStore = void 0;
|
exports.CacheStore = void 0;
|
||||||
var tslib_1 = require("tslib");
|
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 oak_memory_tree_store_1 = require("oak-memory-tree-store");
|
||||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
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) {
|
var CacheStore = /** @class */ (function (_super) {
|
||||||
tslib_1.__extends(CacheStore, _super);
|
tslib_1.__extends(CacheStore, _super);
|
||||||
function CacheStore(storageSchema, getFullDataFn, resetInitialDataFn) {
|
function CacheStore(storageSchema, getFullDataFn, resetInitialDataFn) {
|
||||||
var _this = _super.call(this, storageSchema) || this;
|
var _this = _super.call(this, storageSchema) || this;
|
||||||
_this.checkerExecutor = new CheckerExecutor_1.default();
|
_this.triggerExecutor = new SyncTriggerExecutor_1.default();
|
||||||
_this.getFullDataFn = getFullDataFn;
|
_this.getFullDataFn = getFullDataFn;
|
||||||
_this.resetInitialDataFn = resetInitialDataFn;
|
_this.resetInitialDataFn = resetInitialDataFn;
|
||||||
return _this;
|
return _this;
|
||||||
|
|
@ -21,13 +22,16 @@ var CacheStore = /** @class */ (function (_super) {
|
||||||
CacheStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
|
CacheStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
|
||||||
(0, assert_1.default)(context.getCurrentTxnId());
|
(0, assert_1.default)(context.getCurrentTxnId());
|
||||||
if (!option.blockTrigger) {
|
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 (operation.data) {
|
||||||
if (!option.blockTrigger) {
|
var result = _super.prototype.cascadeUpdate.call(this, entity, operation, context, option);
|
||||||
this.checkerExecutor.check(entity, operation, context, 'after');
|
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) {
|
CacheStore.prototype.operate = function (entity, operation, context, option) {
|
||||||
(0, assert_1.default)(context.getCurrentTxnId());
|
(0, assert_1.default)(context.getCurrentTxnId());
|
||||||
|
|
@ -56,7 +60,15 @@ var CacheStore = /** @class */ (function (_super) {
|
||||||
};
|
};
|
||||||
CacheStore.prototype.check = function (entity, operation, context, checkerTypes) {
|
CacheStore.prototype.check = function (entity, operation, context, checkerTypes) {
|
||||||
(0, assert_1.default)(context.getCurrentTxnId());
|
(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) {
|
CacheStore.prototype.select = function (entity, selection, context, option) {
|
||||||
var autoCommit = !context.getCurrentTxnId();
|
var autoCommit = !context.getCurrentTxnId();
|
||||||
|
|
@ -79,11 +91,11 @@ var CacheStore = /** @class */ (function (_super) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
CacheStore.prototype.registerChecker = function (checker) {
|
CacheStore.prototype.registerChecker = function (checker) {
|
||||||
this.checkerExecutor.registerChecker(checker);
|
this.triggerExecutor.registerChecker(checker);
|
||||||
};
|
|
||||||
CacheStore.prototype.registerGeneralChecker = function (type, fn) {
|
|
||||||
this.checkerExecutor.registerGeneralChecker(type, fn);
|
|
||||||
};
|
};
|
||||||
|
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||||
|
this.triggerExecutor.registerTrigger(trigger);
|
||||||
|
} */
|
||||||
/**
|
/**
|
||||||
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
||||||
* @returns
|
* @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 node_schedule_1 = require("node-schedule");
|
||||||
var constant_1 = require("../constant/constant");
|
var constant_1 = require("../constant/constant");
|
||||||
var DebugStore_1 = require("./DebugStore");
|
var DebugStore_1 = require("./DebugStore");
|
||||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
|
||||||
var assert_1 = require("oak-domain/lib/utils/assert");
|
var assert_1 = require("oak-domain/lib/utils/assert");
|
||||||
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||||
function initDataInStore(store, initialData, stat) {
|
function initDataInStore(store, initialData, stat) {
|
||||||
|
|
@ -387,9 +386,6 @@ function createDebugStore(storageSchema, contextBuilder, triggers, checkers, wat
|
||||||
triggers.forEach(function (ele) { return store.registerTrigger(ele); });
|
triggers.forEach(function (ele) { return store.registerTrigger(ele); });
|
||||||
checkers.forEach(function (ele) { return store.registerChecker(ele); });
|
checkers.forEach(function (ele) { return store.registerChecker(ele); });
|
||||||
(0, assert_1.assert)(actionDict);
|
(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
|
// 如果没有物化数据则使用initialData初始化debugStore
|
||||||
var data = getMaterializedData();
|
var data = getMaterializedData();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|
@ -411,7 +407,7 @@ function createDebugStore(storageSchema, contextBuilder, triggers, checkers, wat
|
||||||
materializeData(data, stat);
|
materializeData(data, stat);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
// 启动watcher
|
// 启动watcher
|
||||||
initializeWatchers(store, contextBuilder, watchers.concat(adWatchers));
|
initializeWatchers(store, contextBuilder, watchers);
|
||||||
// 启动timer
|
// 启动timer
|
||||||
if (timers) {
|
if (timers) {
|
||||||
initializeTimers(store, contextBuilder, timers);
|
initializeTimers(store, contextBuilder, timers);
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
||||||
}>, context: FrontCxt): void;
|
}>, context: FrontCxt): void;
|
||||||
private getInner;
|
private getInner;
|
||||||
get<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context?: FrontCxt, allowMiss?: boolean): Partial<ED[T]["Schema"]>[];
|
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;
|
bindOnSync(callback: (opRecords: OpRecord<ED>[]) => void): void;
|
||||||
unbindOnSync(callback: (opRecords: OpRecord<ED>[]) => void): void;
|
unbindOnSync(callback: (opRecords: OpRecord<ED>[]) => void): void;
|
||||||
getCachedData(): { [T in keyof ED]?: ED[T]["OpSchema"][] | undefined; };
|
getCachedData(): { [T in keyof ED]?: ED[T]["OpSchema"][] | undefined; };
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ var Cache = /** @class */ (function (_super) {
|
||||||
var operation = {
|
var operation = {
|
||||||
action: action,
|
action: action,
|
||||||
filter: filter,
|
filter: filter,
|
||||||
data: data,
|
data: data
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
this.cacheStore.check(entity, operation, context, checkerTypes);
|
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 { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||||
import { Cache } from './cache';
|
import { Cache } from './cache';
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
|
import { RelationAuth } from './relationAuth';
|
||||||
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
|
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
|
||||||
name: string;
|
name: string;
|
||||||
entity: T;
|
entity: T;
|
||||||
|
|
@ -18,8 +19,9 @@ export declare class ContextMenuFactory<ED extends EntityDict & BaseEntityDict,
|
||||||
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||||
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
|
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
|
||||||
cascadePathGraph: AuthCascadePath<ED>[];
|
cascadePathGraph: AuthCascadePath<ED>[];
|
||||||
|
relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>;
|
||||||
private makeMenuWrappers;
|
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;
|
setMenus(menus: IMenu<ED, keyof ED>[]): void;
|
||||||
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string): OMenu[];
|
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) {
|
var ContextMenuFactory = /** @class */ (function (_super) {
|
||||||
tslib_1.__extends(ContextMenuFactory, _super);
|
tslib_1.__extends(ContextMenuFactory, _super);
|
||||||
function ContextMenuFactory(cache, cascadePathGraph) {
|
function ContextMenuFactory(cache, relationAuth, cascadePathGraph) {
|
||||||
var _this = _super.call(this) || this;
|
var _this = _super.call(this) || this;
|
||||||
_this.cache = cache;
|
_this.cache = cache;
|
||||||
_this.cascadePathGraph = cascadePathGraph;
|
_this.cascadePathGraph = cascadePathGraph;
|
||||||
|
_this.relationAuth = relationAuth;
|
||||||
return _this;
|
return _this;
|
||||||
}
|
}
|
||||||
ContextMenuFactory.prototype.makeMenuWrappers = function (menus) {
|
ContextMenuFactory.prototype.makeMenuWrappers = function (menus) {
|
||||||
|
|
@ -95,7 +96,14 @@ var ContextMenuFactory = /** @class */ (function (_super) {
|
||||||
var filters = filtersMaker(entity, entityId);
|
var filters = filtersMaker(entity, entityId);
|
||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
// 这里应该是or关系,paths表达的路径中只要有一条满足就可能满足
|
// 这里应该是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) {
|
if (allows.indexOf(true) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ var geo_1 = require("./geo");
|
||||||
function initialize(aspectWrapper, storageSchema, contextBuilder, store, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, colorDict, makeBridgeUrlFn) {
|
function initialize(aspectWrapper, storageSchema, contextBuilder, store, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, colorDict, makeBridgeUrlFn) {
|
||||||
var cache = new cache_1.Cache(aspectWrapper, contextBuilder, store);
|
var cache = new cache_1.Cache(aspectWrapper, contextBuilder, store);
|
||||||
var location = new location_1.Location();
|
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 runningTree = new runningTree_1.RunningTree(cache, storageSchema, relationAuth);
|
||||||
var locales = new locales_1.Locales(aspectWrapper, makeBridgeUrlFn);
|
var locales = new locales_1.Locales(aspectWrapper, makeBridgeUrlFn);
|
||||||
var geo = new geo_1.Geo(aspectWrapper);
|
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 navigator = new navigator_1.Navigator();
|
||||||
var port = new port_1.Port(aspectWrapper);
|
var port = new port_1.Port(aspectWrapper);
|
||||||
var style = new style_1.Style(colorDict);
|
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 {
|
return {
|
||||||
cache: cache,
|
cache: cache,
|
||||||
location: location,
|
location: location,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||||
import { Cache } from './cache';
|
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 {
|
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 cache;
|
||||||
|
private contextBuilder;
|
||||||
private aspectWrapper;
|
private aspectWrapper;
|
||||||
private actionCascadePathGraph;
|
private actionCascadePathGraph;
|
||||||
private actionCascadePathMap;
|
private actionCascadePathMap;
|
||||||
|
|
@ -15,7 +16,7 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict, Cxt ex
|
||||||
private authDeduceRelationMap;
|
private authDeduceRelationMap;
|
||||||
static IgnoredActions: string[];
|
static IgnoredActions: string[];
|
||||||
private entityGraph?;
|
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;
|
private judgeRelation;
|
||||||
getHasRelationEntities(): string[];
|
getHasRelationEntities(): string[];
|
||||||
getDeduceRelationAttribute(entity: keyof ED): string | undefined;
|
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>[];
|
getCascadeActionAuths(entity: keyof ED, ir: boolean): AuthCascadePath<ED>[];
|
||||||
getCascadeRelationAuthsBySource(entity: keyof ED): AuthCascadePath<ED>[];
|
getCascadeRelationAuthsBySource(entity: keyof ED): AuthCascadePath<ED>[];
|
||||||
getCascadeRelationAuths(entity: keyof ED, ir: boolean): 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>;
|
getRelationIdByName(entity: keyof ED, name: string, entityId?: string): Promise<ED["relation"]["Schema"]["id"] | undefined>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.RelationAuth = void 0;
|
exports.RelationAuth = void 0;
|
||||||
var tslib_1 = require("tslib");
|
var tslib_1 = require("tslib");
|
||||||
|
var types_1 = require("oak-domain/lib/types");
|
||||||
var Feature_1 = require("../types/Feature");
|
var Feature_1 = require("../types/Feature");
|
||||||
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
var RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
var RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
||||||
var relation_1 = require("oak-domain/lib/store/relation");
|
var relation_1 = require("oak-domain/lib/store/relation");
|
||||||
var RelationAuth = /** @class */ (function (_super) {
|
var RelationAuth = /** @class */ (function (_super) {
|
||||||
tslib_1.__extends(RelationAuth, _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;
|
var _this = _super.call(this) || this;
|
||||||
_this.aspectWrapper = aspectWrapper,
|
_this.aspectWrapper = aspectWrapper;
|
||||||
_this.cache = cache;
|
_this.contextBuilder = contextBuilder;
|
||||||
|
_this.cache = cache;
|
||||||
_this.actionCascadePathGraph = actionCascadePathGraph;
|
_this.actionCascadePathGraph = actionCascadePathGraph;
|
||||||
_this.relationCascadePathGraph = relationCascadePathGraph;
|
_this.relationCascadePathGraph = relationCascadePathGraph;
|
||||||
_this.actionCascadePathMap = {};
|
_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]; });
|
var relationAuths = this.relationCascadePathGraph.filter(function (ele) { return ele[0] === entity && ir === ele[3]; });
|
||||||
return relationAuths;
|
return relationAuths;
|
||||||
};
|
};
|
||||||
RelationAuth.prototype.checkRelation = function (entity, operation, context) {
|
RelationAuth.prototype.checkRelation = function (entity, operation) {
|
||||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
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) {
|
RelationAuth.prototype.getRelationIdByName = function (entity, name, entityId) {
|
||||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
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;
|
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;
|
protected getProjection(context?: FrontCxt): ED[T]['Selection']['data'] | undefined;
|
||||||
setProjection(projection: ED[T]['Selection']['data']): void;
|
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 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;
|
protected repel(filter1: ED[T]['Selection']['filter'], filter2: ED[T]['Selection']['filter']): boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { InitializeOptions } from './types/Initialize';
|
||||||
* @param actionDict
|
* @param actionDict
|
||||||
* @returns
|
* @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']>;
|
[T in keyof ED]?: Array<ED[T]['OpSchema']>;
|
||||||
}, option: InitializeOptions<ED>): {
|
}, option: InitializeOptions<ED>): {
|
||||||
features: import("./features").BasicFeatures<ED, Cxt, FrontCxt, CommonAspectDict<ED, Cxt> & AD>;
|
features: import("./features").BasicFeatures<ED, Cxt, FrontCxt, CommonAspectDict<ED, Cxt> & AD>;
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,11 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.initialize = void 0;
|
exports.initialize = void 0;
|
||||||
var tslib_1 = require("tslib");
|
var tslib_1 = require("tslib");
|
||||||
var index_1 = require("oak-domain/lib/checkers/index");
|
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||||
var index_2 = require("oak-domain/lib/triggers/index");
|
|
||||||
var debugStore_1 = require("./debugStore");
|
var debugStore_1 = require("./debugStore");
|
||||||
var features_1 = require("./features");
|
var features_1 = require("./features");
|
||||||
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
var lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
var oak_common_aspect_1 = tslib_1.__importDefault(require("oak-common-aspect"));
|
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 oak_common_aspect_2 = require("oak-common-aspect");
|
||||||
var CacheStore_1 = require("./cacheStore/CacheStore");
|
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"));
|
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 aspectDict2 = Object.assign({}, aspectDict, oak_common_aspect_1.default);
|
||||||
var checkers2 = (checkers).concat((0, index_1.createDynamicCheckers)(storageSchema));
|
var _a = (0, actionDef_1.makeIntrinsicCTWs)(storageSchema, actionDict), intCheckers = _a.checkers, intTriggers = _a.triggers, intWatchers = _a.watchers;
|
||||||
var triggers2 = (0, index_2.createDynamicTriggers)(storageSchema).concat(triggers);
|
var checkers2 = checkers.concat(intCheckers);
|
||||||
var debugStore = (0, debugStore_1.createDebugStore)(storageSchema, backendContextBuilder, triggers2, checkers2, watchers, timers, startRoutines, initialData, actionDict, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
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 cacheStore = new CacheStore_1.CacheStore(storageSchema, function () { return debugStore.getCurrentData(); }, function () { return (0, debugStore_1.clearMaterializedData)(); });
|
||||||
var wrapper = {
|
var wrapper = {
|
||||||
exec: function (name, params) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
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);
|
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); });
|
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 || []);
|
(0, oak_common_aspect_2.registerPorts)(importations || [], exportations || []);
|
||||||
return {
|
return {
|
||||||
features: features,
|
features: features,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ var tslib_1 = require("tslib");
|
||||||
var features_1 = require("./features");
|
var features_1 = require("./features");
|
||||||
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
var actionDef_1 = require("oak-domain/lib/store/actionDef");
|
||||||
var CacheStore_1 = require("./cacheStore/CacheStore");
|
var CacheStore_1 = require("./cacheStore/CacheStore");
|
||||||
var checkers_1 = require("oak-domain/lib/checkers");
|
|
||||||
/**
|
/**
|
||||||
* @param storageSchema
|
* @param storageSchema
|
||||||
* @param createFeatures
|
* @param createFeatures
|
||||||
|
|
@ -22,7 +21,8 @@ var checkers_1 = require("oak-domain/lib/checkers");
|
||||||
function initialize(storageSchema, frontendContextBuilder, connector, checkers, option) {
|
function initialize(storageSchema, frontendContextBuilder, connector, checkers, option) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var actionCascadePathGraph = option.actionCascadePathGraph, relationCascadePathGraph = option.relationCascadePathGraph, authDeduceRelationMap = option.authDeduceRelationMap, actionDict = option.actionDict, selectFreeEntities = option.selectFreeEntities, colorDict = option.colorDict;
|
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 cacheStore = new CacheStore_1.CacheStore(storageSchema);
|
||||||
var wrapper = {
|
var wrapper = {
|
||||||
exec: function (name, params) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
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); });
|
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); });
|
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 {
|
return {
|
||||||
features: features,
|
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 relation_1 = require("oak-domain/lib/store/relation");
|
||||||
var filter_1 = require("oak-domain/lib/store/filter");
|
var filter_1 = require("oak-domain/lib/store/filter");
|
||||||
var runningTree_1 = require("./features/runningTree");
|
var runningTree_1 = require("./features/runningTree");
|
||||||
|
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||||
function onPathSet(option) {
|
function onPathSet(option) {
|
||||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
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;
|
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);
|
var filter = this_1.features.runningTree.getIntrinsticFilters(this_1.state.oakFullpath);
|
||||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||||
// 创建对象的判定不落在具体行上,但要考虑list上外键相关属性的限制
|
// 创建对象的判定不落在具体行上,但要考虑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)) {
|
if (this_1.checkOperation(this_1.state.oakEntity, 'create', data, filter, checkTypes)) {
|
||||||
legalActions.push(action);
|
legalActions.push(action);
|
||||||
}
|
}
|
||||||
|
|
@ -295,9 +296,11 @@ function checkActionsAndCascadeEntities(rows, option) {
|
||||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||||
rows.forEach(function (row) {
|
rows.forEach(function (row) {
|
||||||
var _a;
|
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[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') {
|
if (typeof action === 'object') {
|
||||||
Object.assign(intrinsticData, action.data);
|
Object.assign(intrinsticData, action.data);
|
||||||
}
|
}
|
||||||
|
|
@ -339,9 +342,11 @@ function checkActionsAndCascadeEntities(rows, option) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
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[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') {
|
if (typeof action === 'object') {
|
||||||
Object.assign(intrinsticData, action.data);
|
Object.assign(intrinsticData, action.data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,13 @@ var oakBehavior = Behavior({
|
||||||
return this.features.runningTree.getFreshValue(path2);
|
return this.features.runningTree.getFreshValue(path2);
|
||||||
},
|
},
|
||||||
checkOperation: function (entity, action, data, filter, checkerTypes) {
|
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);
|
return this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||||
},
|
},
|
||||||
tryExecute: function (path) {
|
tryExecute: function (path) {
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,13 @@ var OakComponentBase = /** @class */ (function (_super) {
|
||||||
return this.features.runningTree.getFreshValue(path2);
|
return this.features.runningTree.getFreshValue(path2);
|
||||||
};
|
};
|
||||||
OakComponentBase.prototype.checkOperation = function (entity, action, data, filter, checkerTypes) {
|
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);
|
return this.features.cache.checkOperation(entity, action, data, filter, checkerTypes);
|
||||||
};
|
};
|
||||||
OakComponentBase.prototype.tryExecute = function (path) {
|
OakComponentBase.prototype.tryExecute = function (path) {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
import { AggregationResult, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
import { AggregationResult, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||||
import { StorageSchema } from "oak-domain/lib/types/Storage";
|
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 { 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 { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { SyncContext, SyncRowStore } from 'oak-domain/lib/store/SyncRowStore';
|
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<
|
export class CacheStore<
|
||||||
ED extends EntityDict & BaseEntityDict,
|
ED extends EntityDict & BaseEntityDict,
|
||||||
Cxt extends SyncContext<ED>
|
Cxt extends SyncContext<ED>
|
||||||
> extends TreeStore<ED> implements SyncRowStore<ED, SyncContext<ED>>{
|
> extends TreeStore<ED> implements SyncRowStore<ED, SyncContext<ED>>{
|
||||||
private checkerExecutor: CheckerExecutor<ED, Cxt>;
|
private triggerExecutor: SyncTriggerExecutor<ED, Cxt>;
|
||||||
private getFullDataFn?: () => any;
|
private getFullDataFn?: () => any;
|
||||||
private resetInitialDataFn?: () => void;
|
private resetInitialDataFn?: () => void;
|
||||||
|
|
||||||
|
|
@ -23,7 +26,7 @@ export class CacheStore<
|
||||||
resetInitialDataFn?: () => void
|
resetInitialDataFn?: () => void
|
||||||
) {
|
) {
|
||||||
super(storageSchema);
|
super(storageSchema);
|
||||||
this.checkerExecutor = new CheckerExecutor();
|
this.triggerExecutor = new SyncTriggerExecutor();
|
||||||
this.getFullDataFn = getFullDataFn;
|
this.getFullDataFn = getFullDataFn;
|
||||||
this.resetInitialDataFn = resetInitialDataFn;
|
this.resetInitialDataFn = resetInitialDataFn;
|
||||||
}
|
}
|
||||||
|
|
@ -32,21 +35,24 @@ export class CacheStore<
|
||||||
return this.aggregateSync(entity, aggregation, context, option);
|
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());
|
assert(context.getCurrentTxnId());
|
||||||
if (!option.blockTrigger) {
|
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) {
|
if (!option.blockTrigger) {
|
||||||
this.checkerExecutor.check(entity, operation, context as Cxt, 'after');
|
this.triggerExecutor.check(entity, operation, context as Cxt, 'after', option.checkerTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operate<T extends keyof ED, OP extends TreeStoreOperateOption>(
|
operate<T extends keyof ED, OP extends CachStoreOperation>(
|
||||||
entity: T,
|
entity: T,
|
||||||
operation: ED[T]['Operation'],
|
operation: ED[T]['Operation'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
|
|
@ -65,7 +71,7 @@ export class CacheStore<
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = super.sync<CachStoreOperation, Cxt>(opRecords, context, {});
|
result = super.sync<OperateOption, Cxt>(opRecords, context, {});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (autoCommit) {
|
if (autoCommit) {
|
||||||
context.rollback();
|
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[]) {
|
check<T extends keyof ED>(entity: T, operation: Omit<ED[T]['Operation'], 'id'>, context: Cxt, checkerTypes?: CheckerType[]) {
|
||||||
assert(context.getCurrentTxnId());
|
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<
|
select<
|
||||||
|
|
@ -109,12 +123,12 @@ export class CacheStore<
|
||||||
}
|
}
|
||||||
|
|
||||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>) {
|
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) {
|
/* registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>) {
|
||||||
this.checkerExecutor.registerGeneralChecker(type, fn);
|
this.triggerExecutor.registerTrigger(trigger);
|
||||||
}
|
} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这个函数是在debug下用来获取debugStore的数据,release下不能使用
|
* 这个函数是在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 {
|
import {
|
||||||
Checker, Trigger, StorageSchema, EntityDict, ActionDictOfEntityDict, Watcher, BBWatcher, WBWatcher, Routine, Timer, AuthCascadePath, AuthDeduceRelationMap} from "oak-domain/lib/types";
|
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 { 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 { assert } from 'oak-domain/lib/utils/assert';
|
||||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||||
|
|
@ -268,13 +268,6 @@ export function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt ext
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(actionDict);
|
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
|
// 如果没有物化数据则使用initialData初始化debugStore
|
||||||
const data = getMaterializedData();
|
const data = getMaterializedData();
|
||||||
|
|
@ -299,7 +292,7 @@ export function createDebugStore<ED extends EntityDict & BaseEntityDict, Cxt ext
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
// 启动watcher
|
// 启动watcher
|
||||||
initializeWatchers(store, contextBuilder, watchers.concat(adWatchers));
|
initializeWatchers(store, contextBuilder, watchers);
|
||||||
// 启动timer
|
// 启动timer
|
||||||
if (timers) {
|
if (timers) {
|
||||||
initializeTimers(store, contextBuilder, timers);
|
initializeTimers(store, contextBuilder, timers);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { OakRowUnexistedException, OakRowInconsistencyException, OakException, O
|
||||||
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
||||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||||
|
|
||||||
export class Cache<
|
export class Cache<
|
||||||
ED extends EntityDict & BaseEntityDict,
|
ED extends EntityDict & BaseEntityDict,
|
||||||
|
|
@ -210,7 +211,7 @@ export class Cache<
|
||||||
const operation = {
|
const operation = {
|
||||||
action,
|
action,
|
||||||
filter,
|
filter,
|
||||||
data,
|
data
|
||||||
} as ED[T]['Update'];
|
} as ED[T]['Update'];
|
||||||
try {
|
try {
|
||||||
this.cacheStore!.check(entity, operation, context, checkerTypes);
|
this.cacheStore!.check(entity, operation, context, checkerTypes);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import { combineFilters } from 'oak-domain/lib/store/filter';
|
||||||
import { Cache } from './cache';
|
import { Cache } from './cache';
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
import { judgeRelation } from 'oak-domain/lib/store/relation';
|
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> {
|
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -30,6 +32,7 @@ export class ContextMenuFactory<
|
||||||
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||||
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
|
menuWrappers?: IMenuWrapper<ED, keyof ED>[];
|
||||||
cascadePathGraph: AuthCascadePath<ED>[];
|
cascadePathGraph: AuthCascadePath<ED>[];
|
||||||
|
relationAuth: RelationAuth<ED, Cxt, FrontCxt, AD>;
|
||||||
|
|
||||||
private makeMenuWrappers(menus: IMenu<ED, keyof ED>[]): IMenuWrapper<ED, keyof ED>[] {
|
private makeMenuWrappers(menus: IMenu<ED, keyof ED>[]): IMenuWrapper<ED, keyof ED>[] {
|
||||||
const destEntities = uniq(
|
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();
|
super();
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.cascadePathGraph = cascadePathGraph;
|
this.cascadePathGraph = cascadePathGraph;
|
||||||
|
this.relationAuth = relationAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMenus(menus: IMenu<ED, keyof ED>[]) {
|
setMenus(menus: IMenu<ED, keyof ED>[]) {
|
||||||
|
|
@ -132,7 +136,14 @@ export class ContextMenuFactory<
|
||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
// 这里应该是or关系,paths表达的路径中只要有一条满足就可能满足
|
// 这里应该是or关系,paths表达的路径中只要有一条满足就可能满足
|
||||||
const allows = filters.map(
|
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) {
|
if (allows.indexOf(true) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export function initialize<ED extends EntityDict & BaseEntityDict, Cxt extends A
|
||||||
makeBridgeUrlFn?: (url: string, headers?: Record<string, string>) => string) {
|
makeBridgeUrlFn?: (url: string, headers?: Record<string, string>) => string) {
|
||||||
const cache = new Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper, contextBuilder, store);
|
const cache = new Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper, contextBuilder, store);
|
||||||
const location = new Location();
|
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);
|
actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities);
|
||||||
const runningTree = new RunningTree<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(cache, storageSchema, relationAuth);
|
const runningTree = new RunningTree<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>(cache, storageSchema, relationAuth);
|
||||||
const locales = new Locales(aspectWrapper, makeBridgeUrlFn);
|
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 navigator = new Navigator();
|
||||||
const port = new Port<ED, Cxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper);
|
const port = new Port<ED, Cxt, AD & CommonAspectDict<ED, Cxt>>(aspectWrapper);
|
||||||
const style = new Style<ED>(colorDict);
|
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 {
|
return {
|
||||||
cache,
|
cache,
|
||||||
location,
|
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 { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { CommonAspectDict } from 'oak-common-aspect';
|
import { CommonAspectDict } from 'oak-common-aspect';
|
||||||
import { Feature } from '../types/Feature';
|
import { Feature } from '../types/Feature';
|
||||||
|
|
@ -17,6 +17,7 @@ export class RelationAuth<
|
||||||
AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>
|
AD extends CommonAspectDict<ED, Cxt> & Record<string, Aspect<ED, Cxt>>
|
||||||
> extends Feature {
|
> extends Feature {
|
||||||
private cache: Cache<ED, Cxt, FrontCxt, AD>;
|
private cache: Cache<ED, Cxt, FrontCxt, AD>;
|
||||||
|
private contextBuilder: () => FrontCxt;
|
||||||
private aspectWrapper: AspectWrapper<ED, Cxt, AD>;
|
private aspectWrapper: AspectWrapper<ED, Cxt, AD>;
|
||||||
private actionCascadePathGraph: AuthCascadePath<ED>[];
|
private actionCascadePathGraph: AuthCascadePath<ED>[];
|
||||||
private actionCascadePathMap: Record<string, AuthCascadePath<ED>[]>;
|
private actionCascadePathMap: Record<string, AuthCascadePath<ED>[]>;
|
||||||
|
|
@ -35,6 +36,7 @@ export class RelationAuth<
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
aspectWrapper: AspectWrapper<ED, Cxt, AD>,
|
aspectWrapper: AspectWrapper<ED, Cxt, AD>,
|
||||||
|
contextBuilder: () => FrontCxt,
|
||||||
cache: Cache<ED, Cxt, FrontCxt, AD>,
|
cache: Cache<ED, Cxt, FrontCxt, AD>,
|
||||||
actionCascadePathGraph: AuthCascadePath<ED>[],
|
actionCascadePathGraph: AuthCascadePath<ED>[],
|
||||||
relationCascadePathGraph: AuthCascadePath<ED>[],
|
relationCascadePathGraph: AuthCascadePath<ED>[],
|
||||||
|
|
@ -42,7 +44,8 @@ export class RelationAuth<
|
||||||
selectFreeEntities: (keyof ED)[],
|
selectFreeEntities: (keyof ED)[],
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.aspectWrapper = aspectWrapper,
|
this.aspectWrapper = aspectWrapper;
|
||||||
|
this.contextBuilder = contextBuilder;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.actionCascadePathGraph = actionCascadePathGraph;
|
this.actionCascadePathGraph = actionCascadePathGraph;
|
||||||
this.relationCascadePathGraph = relationCascadePathGraph;
|
this.relationCascadePathGraph = relationCascadePathGraph;
|
||||||
|
|
@ -223,8 +226,21 @@ export class RelationAuth<
|
||||||
return relationAuths;
|
return relationAuths;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRelation<T extends keyof ED>(entity: T, operation: ED[T]['Operation'] | ED[T]['Selection'], context: FrontCxt) {
|
checkRelation<T extends keyof ED>(entity: T, operation: Omit< ED[T]['Operation'] | ED[T]['Selection'], 'id'>) {
|
||||||
this.baseRelationAuth.checkRelationSync(entity, operation, context);
|
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) {
|
async getRelationIdByName(entity: keyof ED, name: string, entityId?: string) {
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,13 @@ import {
|
||||||
} from 'oak-domain/lib/types';
|
} from 'oak-domain/lib/types';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||||
import { createDynamicCheckers } from 'oak-domain/lib/checkers/index';
|
import { makeIntrinsicCTWs } from 'oak-domain/lib/store/actionDef';
|
||||||
import { createDynamicTriggers } from 'oak-domain/lib/triggers/index';
|
|
||||||
|
|
||||||
import { createDebugStore, clearMaterializedData } from './debugStore';
|
import { createDebugStore, clearMaterializedData } from './debugStore';
|
||||||
|
|
||||||
import { initialize as initBasicFeatures } from './features';
|
import { initialize as initBasicFeatures } from './features';
|
||||||
import { intersection } from 'oak-domain/lib/utils/lodash';
|
import { intersection } from 'oak-domain/lib/utils/lodash';
|
||||||
import commonAspectDict from 'oak-common-aspect';
|
import commonAspectDict from 'oak-common-aspect';
|
||||||
import { analyzeActionDefDict } from 'oak-domain/lib/store/actionDef';
|
|
||||||
import { CommonAspectDict, registerPorts } from 'oak-common-aspect';
|
import { CommonAspectDict, registerPorts } from 'oak-common-aspect';
|
||||||
import { CacheStore } from './cacheStore/CacheStore';
|
import { CacheStore } from './cacheStore/CacheStore';
|
||||||
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
|
||||||
|
|
@ -49,7 +47,7 @@ export function initialize<
|
||||||
frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt,
|
frontendContextBuilder: () => (store: CacheStore<ED, FrontCxt>) => FrontCxt,
|
||||||
backendContextBuilder: (contextStr?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>,
|
backendContextBuilder: (contextStr?: string) => (store: DebugStore<ED, Cxt>) => Promise<Cxt>,
|
||||||
aspectDict: AD,
|
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>>,
|
checkers: Array<Checker<ED, keyof ED, FrontCxt | Cxt>>,
|
||||||
watchers: Array<Watcher<ED, keyof ED, Cxt>>,
|
watchers: Array<Watcher<ED, keyof ED, Cxt>>,
|
||||||
timers: Array<Timer<ED, Cxt>>,
|
timers: Array<Timer<ED, Cxt>>,
|
||||||
|
|
@ -68,14 +66,16 @@ export function initialize<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const aspectDict2 = Object.assign({}, aspectDict, commonAspectDict);
|
const aspectDict2 = Object.assign({}, aspectDict, commonAspectDict);
|
||||||
const checkers2 = (checkers).concat(createDynamicCheckers<ED, Cxt | FrontCxt>(storageSchema));
|
const { checkers: intCheckers, triggers: intTriggers, watchers: intWatchers } = makeIntrinsicCTWs<ED, Cxt, FrontCxt>(storageSchema, actionDict);
|
||||||
const triggers2 = createDynamicTriggers<ED, Cxt>(storageSchema).concat(triggers);
|
const checkers2 = checkers.concat(intCheckers);
|
||||||
|
const triggers2 = triggers.concat(intTriggers);
|
||||||
|
const watchers2 = watchers.concat(intWatchers);
|
||||||
const debugStore = createDebugStore(
|
const debugStore = createDebugStore(
|
||||||
storageSchema,
|
storageSchema,
|
||||||
backendContextBuilder,
|
backendContextBuilder,
|
||||||
triggers2,
|
triggers2,
|
||||||
checkers2,
|
checkers2,
|
||||||
watchers,
|
watchers2,
|
||||||
timers,
|
timers,
|
||||||
startRoutines,
|
startRoutines,
|
||||||
initialData,
|
initialData,
|
||||||
|
|
@ -125,15 +125,7 @@ export function initialize<
|
||||||
selectFreeEntities,
|
selectFreeEntities,
|
||||||
colorDict);
|
colorDict);
|
||||||
|
|
||||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker as Checker<ED, keyof ED, SyncContext<ED>>));
|
checkers2.forEach((checker) => cacheStore.registerChecker(checker));
|
||||||
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));
|
|
||||||
|
|
||||||
registerPorts(importations || [], exportations || []);
|
registerPorts(importations || [], exportations || []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ import {
|
||||||
Checker,
|
Checker,
|
||||||
StorageSchema,
|
StorageSchema,
|
||||||
Connector,
|
Connector,
|
||||||
|
Trigger,
|
||||||
} from 'oak-domain/lib/types';
|
} from 'oak-domain/lib/types';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||||
|
|
||||||
import { initialize as initBasicFeatures } from './features';
|
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 { CommonAspectDict } from 'oak-common-aspect';
|
||||||
import { CacheStore } from './cacheStore/CacheStore';
|
import { CacheStore } from './cacheStore/CacheStore';
|
||||||
import { createDynamicCheckers } from 'oak-domain/lib/checkers';
|
import { createDynamicCheckers } from 'oak-domain/lib/checkers';
|
||||||
|
|
@ -43,7 +44,10 @@ export function initialize<
|
||||||
option: InitializeOptions<ED>
|
option: InitializeOptions<ED>
|
||||||
) {
|
) {
|
||||||
const { actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, actionDict, selectFreeEntities, colorDict } = option;
|
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>(
|
const cacheStore = new CacheStore<ED, FrontCxt>(
|
||||||
storageSchema,
|
storageSchema,
|
||||||
|
|
@ -74,15 +78,7 @@ export function initialize<
|
||||||
(url, headers) => connector.makeBridgeUrl(url, headers)
|
(url, headers) => connector.makeBridgeUrl(url, headers)
|
||||||
);
|
);
|
||||||
|
|
||||||
checkers2.forEach((checker) => cacheStore.registerChecker(checker as Checker<ED, keyof ED, SyncContext<ED>>));
|
checkers2.forEach((checker) => cacheStore.registerChecker(checker));
|
||||||
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));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
features,
|
features,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { MessageProps } from './types/Message';
|
||||||
import { judgeRelation } from 'oak-domain/lib/store/relation';
|
import { judgeRelation } from 'oak-domain/lib/store/relation';
|
||||||
import { addFilterSegment, combineFilters } from 'oak-domain/lib/store/filter';
|
import { addFilterSegment, combineFilters } from 'oak-domain/lib/store/filter';
|
||||||
import { MODI_NEXT_PATH_SUFFIX } from './features/runningTree';
|
import { MODI_NEXT_PATH_SUFFIX } from './features/runningTree';
|
||||||
|
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||||
|
|
||||||
export async function onPathSet<
|
export async function onPathSet<
|
||||||
ED extends EntityDict & BaseEntityDict,
|
ED extends EntityDict & BaseEntityDict,
|
||||||
|
|
@ -174,7 +175,7 @@ function checkActionsAndCascadeEntities<
|
||||||
const filter = this.features.runningTree.getIntrinsticFilters(this.state.oakFullpath!);
|
const filter = this.features.runningTree.getIntrinsticFilters(this.state.oakFullpath!);
|
||||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||||
// 创建对象的判定不落在具体行上,但要考虑list上外键相关属性的限制
|
// 创建对象的判定不落在具体行上,但要考虑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)) {
|
if (this.checkOperation(this.state.oakEntity, 'create', data as any, filter, checkTypes)) {
|
||||||
legalActions.push(action);
|
legalActions.push(action);
|
||||||
}
|
}
|
||||||
|
|
@ -289,8 +290,9 @@ function checkActionsAndCascadeEntities<
|
||||||
rows.forEach(
|
rows.forEach(
|
||||||
(row) => {
|
(row) => {
|
||||||
const intrinsticData = rel[1] ? {
|
const intrinsticData = rel[1] ? {
|
||||||
|
id: generateNewId(),
|
||||||
[rel[1]]: row.id,
|
[rel[1]]: row.id,
|
||||||
} : { entity: this.state.oakEntity, entityId: row.id };
|
} : { id: generateNewId(), entity: this.state.oakEntity, entityId: row.id };
|
||||||
if (typeof action === 'object') {
|
if (typeof action === 'object') {
|
||||||
Object.assign(intrinsticData, action.data);
|
Object.assign(intrinsticData, action.data);
|
||||||
}
|
}
|
||||||
|
|
@ -338,8 +340,9 @@ function checkActionsAndCascadeEntities<
|
||||||
else {
|
else {
|
||||||
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
if (action === 'create' || typeof action === 'object' && action.action === 'create') {
|
||||||
const intrinsticData = rel[1] ? {
|
const intrinsticData = rel[1] ? {
|
||||||
|
id: generateNewId(),
|
||||||
[rel[1]]: rows.id,
|
[rel[1]]: rows.id,
|
||||||
} : { entity: this.state.oakEntity, entityId: rows.id };
|
} : { id: generateNewId(), entity: this.state.oakEntity, entityId: rows.id };
|
||||||
if (typeof action === 'object') {
|
if (typeof action === 'object') {
|
||||||
Object.assign(intrinsticData, action.data);
|
Object.assign(intrinsticData, action.data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,22 @@ const oakBehavior = Behavior<
|
||||||
},
|
},
|
||||||
|
|
||||||
checkOperation(entity, action, data, filter, checkerTypes) {
|
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(
|
return this.features.cache.checkOperation(
|
||||||
entity,
|
entity,
|
||||||
action,
|
action,
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,22 @@ abstract class OakComponentBase<
|
||||||
filter?: ED[T]['Update']['filter'],
|
filter?: ED[T]['Update']['filter'],
|
||||||
checkerTypes?: CheckerType[]
|
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(
|
return this.features.cache.checkOperation(
|
||||||
entity,
|
entity,
|
||||||
action,
|
action,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue