Merge branch 'release'
This commit is contained in:
commit
c98a8c43ea
|
|
@ -12,7 +12,8 @@ exports.desc = {
|
||||||
type: "varchar",
|
type: "varchar",
|
||||||
params: {
|
params: {
|
||||||
length: 32
|
length: 32
|
||||||
}
|
},
|
||||||
|
ref: ["user"]
|
||||||
},
|
},
|
||||||
entityId: {
|
entityId: {
|
||||||
type: "varchar",
|
type: "varchar",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ exports.desc = {
|
||||||
type: "varchar",
|
type: "varchar",
|
||||||
params: {
|
params: {
|
||||||
length: 32
|
length: 32
|
||||||
}
|
},
|
||||||
|
ref: ["modi", "user"]
|
||||||
},
|
},
|
||||||
entityId: {
|
entityId: {
|
||||||
type: "varchar",
|
type: "varchar",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ var modi_1 = require("../store/modi");
|
||||||
function createDynamicCheckers(schema, authDict) {
|
function createDynamicCheckers(schema, authDict) {
|
||||||
var checkers = [];
|
var checkers = [];
|
||||||
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
|
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
|
||||||
|
// checkers.push(...createRemoveCheckers<ED, Cxt>(schema));
|
||||||
if (authDict) {
|
if (authDict) {
|
||||||
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
|
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2871,6 +2871,14 @@ function constructAttributes(entity) {
|
||||||
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
|
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
|
||||||
factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(typeArguments[0].literal.text)),
|
factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(typeArguments[0].literal.text)),
|
||||||
], true)));
|
], true)));
|
||||||
|
// 如果是entity,在这里处理一下ref
|
||||||
|
if (ts.isIdentifier(name) && name.text === 'entity') {
|
||||||
|
var mtoRelations = ReversePointerRelations[entity];
|
||||||
|
if (mtoRelations) {
|
||||||
|
var mtoEntities = mtoRelations.map(function (ele) { return (0, string_1.firstLetterLowerCase)(ele); });
|
||||||
|
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("ref"), factory.createArrayLiteralExpression(mtoEntities.map(function (ele) { return factory.createStringLiteral(ele); }), false)));
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'Text':
|
case 'Text':
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
var lodash_1 = require("../utils/lodash");
|
var lodash_1 = require("../utils/lodash");
|
||||||
var filter_1 = require("../store/filter");
|
var filter_1 = require("../store/filter");
|
||||||
var Entity_1 = require("../types/Entity");
|
var Entity_1 = require("../types/Entity");
|
||||||
|
var Trigger_1 = require("../types/Trigger");
|
||||||
var SyncRowStore_1 = require("./SyncRowStore");
|
var SyncRowStore_1 = require("./SyncRowStore");
|
||||||
var checker_1 = require("./checker");
|
var checker_1 = require("./checker");
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,10 +34,11 @@ var TriggerExecutor = /** @class */ (function () {
|
||||||
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
|
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
|
||||||
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
|
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
|
||||||
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
|
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
|
||||||
|
var priority = type === 'data' ? Trigger_1.DATA_CHECKER_DEFAULT_PRIORITY : Trigger_1.CHECKER_DEFAULT_PRIORITY; // checker的默认优先级最低(前面的trigger可能会赋上一些相应的值)
|
||||||
var trigger = {
|
var trigger = {
|
||||||
checkerType: type,
|
checkerType: type,
|
||||||
name: triggerName,
|
name: triggerName,
|
||||||
priority: checker.priority || 20,
|
priority: checker.priority || priority,
|
||||||
entity: entity,
|
entity: entity,
|
||||||
action: action,
|
action: action,
|
||||||
fn: fn,
|
fn: fn,
|
||||||
|
|
@ -59,7 +61,10 @@ var TriggerExecutor = /** @class */ (function () {
|
||||||
throw new Error("\u4E0D\u53EF\u6709\u540C\u540D\u7684\u89E6\u53D1\u5668\u300C".concat(trigger.name, "\u300D"));
|
throw new Error("\u4E0D\u53EF\u6709\u540C\u540D\u7684\u89E6\u53D1\u5668\u300C".concat(trigger.name, "\u300D"));
|
||||||
}
|
}
|
||||||
if (typeof trigger.priority !== 'number') {
|
if (typeof trigger.priority !== 'number') {
|
||||||
trigger.priority = 10; // 默认值
|
trigger.priority = Trigger_1.TRIGGER_DEFAULT_PRIORITY; // 默认值
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.default)(trigger.priority <= Trigger_1.TRIGGER_MAX_PRIORITY && trigger.priority >= Trigger_1.TRIGGER_MIN_PRIORITY, "trigger\u300C".concat(trigger.name, "\u300D\u7684\u4F18\u5148\u7EA7\u5B9A\u4E49\u8D8A\u754C\uFF0C\u5E94\u8BE5\u5728").concat(Trigger_1.TRIGGER_MIN_PRIORITY, "\u5230").concat(Trigger_1.TRIGGER_MAX_PRIORITY, "\u4E4B\u95F4"));
|
||||||
}
|
}
|
||||||
if (trigger.filter) {
|
if (trigger.filter) {
|
||||||
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
|
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,17 @@ export declare function translateCheckerInSyncContext<ED extends EntityDict & Ba
|
||||||
fn: (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
fn: (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
||||||
when: 'before' | 'after';
|
when: 'before' | 'after';
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 根据权限定义,创建出相应的checker
|
||||||
|
* @param schema
|
||||||
|
* @param authDict
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||||
|
/**
|
||||||
|
* 对对象的删除,检查其是否会产生其他行上的空指针,不允许这种情况的出现
|
||||||
|
* @param schema
|
||||||
|
* @returns
|
||||||
|
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||||
|
*/
|
||||||
|
export declare function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
exports.createRemoveCheckers = exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
||||||
var tslib_1 = require("tslib");
|
var tslib_1 = require("tslib");
|
||||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
var filter_1 = require("../store/filter");
|
var filter_1 = require("../store/filter");
|
||||||
|
|
@ -392,6 +392,12 @@ function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
||||||
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||||
return function (userId) { return filterMaker(userId); };
|
return function (userId) { return filterMaker(userId); };
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 根据权限定义,创建出相应的checker
|
||||||
|
* @param schema
|
||||||
|
* @param authDict
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function createAuthCheckers(schema, authDict) {
|
function createAuthCheckers(schema, authDict) {
|
||||||
var checkers = [];
|
var checkers = [];
|
||||||
var _loop_1 = function (entity) {
|
var _loop_1 = function (entity) {
|
||||||
|
|
@ -496,3 +502,224 @@ function createAuthCheckers(schema, authDict) {
|
||||||
return checkers;
|
return checkers;
|
||||||
}
|
}
|
||||||
exports.createAuthCheckers = createAuthCheckers;
|
exports.createAuthCheckers = createAuthCheckers;
|
||||||
|
/**
|
||||||
|
* 对对象的删除,检查其是否会产生其他行上的空指针,不允许这种情况的出现
|
||||||
|
* @param schema
|
||||||
|
* @returns
|
||||||
|
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||||
|
*/
|
||||||
|
function createRemoveCheckers(schema) {
|
||||||
|
var e_1, _a;
|
||||||
|
var checkers = [];
|
||||||
|
// 先建立所有的一对多的关系
|
||||||
|
var OneToManyMatrix = {};
|
||||||
|
var OneToManyOnEntityMatrix = {};
|
||||||
|
var addToMto = function (e, f, attr) {
|
||||||
|
var _a;
|
||||||
|
if (OneToManyMatrix[f]) {
|
||||||
|
(_a = OneToManyMatrix[f]) === null || _a === void 0 ? void 0 : _a.push([e, attr]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
OneToManyMatrix[f] = [[e, attr]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var addToMtoEntity = function (e, fs) {
|
||||||
|
var e_2, _a;
|
||||||
|
var _b;
|
||||||
|
try {
|
||||||
|
for (var fs_1 = tslib_1.__values(fs), fs_1_1 = fs_1.next(); !fs_1_1.done; fs_1_1 = fs_1.next()) {
|
||||||
|
var f = fs_1_1.value;
|
||||||
|
if (!OneToManyOnEntityMatrix[f]) {
|
||||||
|
OneToManyOnEntityMatrix[f] = [e];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(_b = OneToManyOnEntityMatrix[f]) === null || _b === void 0 ? void 0 : _b.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (fs_1_1 && !fs_1_1.done && (_a = fs_1.return)) _a.call(fs_1);
|
||||||
|
}
|
||||||
|
finally { if (e_2) throw e_2.error; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (var entity in schema) {
|
||||||
|
if (['operEntity'].includes(entity)) {
|
||||||
|
continue; // OperEntity会指向每一个对象,不必处理
|
||||||
|
}
|
||||||
|
var attributes = schema[entity].attributes;
|
||||||
|
for (var attr in attributes) {
|
||||||
|
if (attributes[attr].type === 'ref') {
|
||||||
|
addToMto(entity, attributes[attr].ref, attr);
|
||||||
|
}
|
||||||
|
else if (attr === 'entity') {
|
||||||
|
if (attributes[attr].ref) {
|
||||||
|
addToMtoEntity(entity, attributes[attr].ref);
|
||||||
|
}
|
||||||
|
else if (process.env.NODE_ENV === 'development') {
|
||||||
|
console.warn("".concat(entity, "\u7684entity\u53CD\u6307\u6307\u9488\u627E\u4E0D\u5230\u6709\u6548\u7684\u5BF9\u8C61"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 当删除一时,要确认多上面没有指向一的数据
|
||||||
|
var entities = (0, lodash_1.intersection)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
||||||
|
var _loop_3 = function (entity) {
|
||||||
|
checkers.push({
|
||||||
|
entity: entity,
|
||||||
|
action: 'remove',
|
||||||
|
type: 'logical',
|
||||||
|
checker: function (operation, context, option) {
|
||||||
|
var e_3, _a, e_4, _b;
|
||||||
|
var promises = [];
|
||||||
|
if (OneToManyMatrix[entity]) {
|
||||||
|
var _loop_4 = function (otm) {
|
||||||
|
var _g, _h, _j, _k;
|
||||||
|
var _l = tslib_1.__read(otm, 2), e = _l[0], attr = _l[1];
|
||||||
|
var proj = (_g = {
|
||||||
|
id: 1
|
||||||
|
},
|
||||||
|
_g[attr] = 1,
|
||||||
|
_g);
|
||||||
|
var filter = operation.filter && (_h = {},
|
||||||
|
_h[attr.slice(0, attr.length - 2)] = operation.filter,
|
||||||
|
_h);
|
||||||
|
var result = context.select(e, {
|
||||||
|
data: proj,
|
||||||
|
filter: filter,
|
||||||
|
indexFrom: 0,
|
||||||
|
count: 1
|
||||||
|
}, { dontCollect: true });
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
promises.push(result.then(function (_a) {
|
||||||
|
var _b, _c;
|
||||||
|
var _d = tslib_1.__read(_a, 1), row = _d[0];
|
||||||
|
if (row) {
|
||||||
|
var record = {
|
||||||
|
a: 's',
|
||||||
|
d: (_b = {},
|
||||||
|
_b[e] = (_c = {},
|
||||||
|
_c[row.id] = row,
|
||||||
|
_c),
|
||||||
|
_b)
|
||||||
|
};
|
||||||
|
throw new Exception_1.OakRowInconsistencyException(record, "\u60A8\u65E0\u6CD5\u5220\u9664\u5B58\u5728\u6709\u6548\u6570\u636E\u300C".concat(e, "\u300D\u5173\u8054\u7684\u884C"));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var _m = tslib_1.__read(result, 1), row = _m[0];
|
||||||
|
if (row) {
|
||||||
|
var record = {
|
||||||
|
a: 's',
|
||||||
|
d: (_j = {},
|
||||||
|
_j[e] = (_k = {},
|
||||||
|
_k[row.id] = row,
|
||||||
|
_k),
|
||||||
|
_j)
|
||||||
|
};
|
||||||
|
throw new Exception_1.OakRowInconsistencyException(record, "\u60A8\u65E0\u6CD5\u5220\u9664\u5B58\u5728\u6709\u6548\u6570\u636E\u300C".concat(e, "\u300D\u5173\u8054\u7684\u884C"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
for (var _c = (e_3 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||||
|
var otm = _d.value;
|
||||||
|
_loop_4(otm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
||||||
|
}
|
||||||
|
finally { if (e_3) throw e_3.error; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (OneToManyOnEntityMatrix[entity]) {
|
||||||
|
var _loop_5 = function (otm) {
|
||||||
|
var _o, _p, _q;
|
||||||
|
var proj = {
|
||||||
|
id: 1,
|
||||||
|
entity: 1,
|
||||||
|
entityId: 1,
|
||||||
|
};
|
||||||
|
var filter = operation.filter && (_o = {},
|
||||||
|
_o[entity] = operation.filter,
|
||||||
|
_o);
|
||||||
|
var result = context.select(otm, {
|
||||||
|
data: proj,
|
||||||
|
filter: filter,
|
||||||
|
indexFrom: 0,
|
||||||
|
count: 1
|
||||||
|
}, { dontCollect: true });
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
promises.push(result.then(function (_a) {
|
||||||
|
var _b, _c;
|
||||||
|
var _d = tslib_1.__read(_a, 1), row = _d[0];
|
||||||
|
if (row) {
|
||||||
|
var record = {
|
||||||
|
a: 's',
|
||||||
|
d: (_b = {},
|
||||||
|
_b[otm] = (_c = {},
|
||||||
|
_c[row.id] = row,
|
||||||
|
_c),
|
||||||
|
_b)
|
||||||
|
};
|
||||||
|
throw new Exception_1.OakRowInconsistencyException(record, "\u60A8\u65E0\u6CD5\u5220\u9664\u5B58\u5728\u6709\u6548\u6570\u636E\u300C".concat(otm, "\u300D\u5173\u8054\u7684\u884C"));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var _r = tslib_1.__read(result, 1), row = _r[0];
|
||||||
|
if (row) {
|
||||||
|
var record = {
|
||||||
|
a: 's',
|
||||||
|
d: (_p = {},
|
||||||
|
_p[otm] = (_q = {},
|
||||||
|
_q[row.id] = row,
|
||||||
|
_q),
|
||||||
|
_p)
|
||||||
|
};
|
||||||
|
throw new Exception_1.OakRowInconsistencyException(record, "\u60A8\u65E0\u6CD5\u5220\u9664\u5B58\u5728\u6709\u6548\u6570\u636E\u300C".concat(otm, "\u300D\u5173\u8054\u7684\u884C"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
for (var _e = (e_4 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
|
||||||
|
var otm = _f.value;
|
||||||
|
_loop_5(otm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
||||||
|
}
|
||||||
|
finally { if (e_4) throw e_4.error; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (promises.length > 0) {
|
||||||
|
return Promise.all(promises).then(function () { return undefined; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
for (var entities_1 = tslib_1.__values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
|
||||||
|
var entity = entities_1_1.value;
|
||||||
|
_loop_3(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
||||||
|
}
|
||||||
|
finally { if (e_1) throw e_1.error; }
|
||||||
|
}
|
||||||
|
return checkers;
|
||||||
|
}
|
||||||
|
exports.createRemoveCheckers = createRemoveCheckers;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export declare type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt e
|
||||||
type: 'data';
|
type: 'data';
|
||||||
entity: T;
|
entity: T;
|
||||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void | Promise<void>;
|
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||||
};
|
};
|
||||||
export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||||
|
|
@ -44,7 +44,7 @@ export declare type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cx
|
||||||
when?: 'after';
|
when?: 'after';
|
||||||
entity: T;
|
entity: T;
|
||||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
|
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||||
};
|
};
|
||||||
export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||||
|
|
@ -53,7 +53,7 @@ export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyo
|
||||||
when?: 'after';
|
when?: 'after';
|
||||||
entity: T;
|
entity: T;
|
||||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
|
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||||
};
|
};
|
||||||
export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt> | LogicalRelationChecker<ED, T, Cxt>;
|
export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt> | LogicalRelationChecker<ED, T, Cxt>;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export interface Index<SH extends EntityShape> {
|
||||||
export interface Attribute {
|
export interface Attribute {
|
||||||
type: DataType | Ref;
|
type: DataType | Ref;
|
||||||
params?: DataTypeParams;
|
params?: DataTypeParams;
|
||||||
ref?: string;
|
ref?: string | string[];
|
||||||
onRefDelete?: 'delete' | 'setNull' | 'ignore';
|
onRefDelete?: 'delete' | 'setNull' | 'ignore';
|
||||||
default?: string | number | boolean;
|
default?: string | number | boolean;
|
||||||
notNull?: boolean;
|
notNull?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,14 @@ import { AsyncContext } from "../store/AsyncRowStore";
|
||||||
import { SyncContext } from "../store/SyncRowStore";
|
import { SyncContext } from "../store/SyncRowStore";
|
||||||
import { EntityDict, OperateOption } from "../types/Entity";
|
import { EntityDict, OperateOption } from "../types/Entity";
|
||||||
import { EntityShape } from "../types/Entity";
|
import { EntityShape } from "../types/Entity";
|
||||||
|
/**
|
||||||
|
* 优先级越小,越早执行。定义在1~99之间
|
||||||
|
*/
|
||||||
|
export declare const TRIGGER_DEFAULT_PRIORITY = 50;
|
||||||
|
export declare const TRIGGER_MIN_PRIORITY = 1;
|
||||||
|
export declare const TRIGGER_MAX_PRIORITY = 99;
|
||||||
|
export declare const DATA_CHECKER_DEFAULT_PRIORITY = 60;
|
||||||
|
export declare const CHECKER_DEFAULT_PRIORITY = 99;
|
||||||
interface TriggerBase<ED extends EntityDict, T extends keyof ED> {
|
interface TriggerBase<ED extends EntityDict, T extends keyof ED> {
|
||||||
checkerType?: CheckerType;
|
checkerType?: CheckerType;
|
||||||
entity: T;
|
entity: T;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.CHECKER_DEFAULT_PRIORITY = exports.DATA_CHECKER_DEFAULT_PRIORITY = exports.TRIGGER_MAX_PRIORITY = exports.TRIGGER_MIN_PRIORITY = exports.TRIGGER_DEFAULT_PRIORITY = void 0;
|
||||||
|
/**
|
||||||
|
* 优先级越小,越早执行。定义在1~99之间
|
||||||
|
*/
|
||||||
|
exports.TRIGGER_DEFAULT_PRIORITY = 50;
|
||||||
|
exports.TRIGGER_MIN_PRIORITY = 1;
|
||||||
|
exports.TRIGGER_MAX_PRIORITY = 99;
|
||||||
|
exports.DATA_CHECKER_DEFAULT_PRIORITY = 60;
|
||||||
|
exports.CHECKER_DEFAULT_PRIORITY = 99;
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-domain",
|
"name": "oak-domain",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "XuChang"
|
"name": "XuChang"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { EntityDict } from '../base-app-domain';
|
import { EntityDict } from '../base-app-domain';
|
||||||
import { AsyncContext } from '../store/AsyncRowStore';
|
import { AsyncContext } from '../store/AsyncRowStore';
|
||||||
import { createAuthCheckers } from '../store/checker';
|
import { createAuthCheckers, createRemoveCheckers } from '../store/checker';
|
||||||
import { createModiRelatedCheckers } from '../store/modi';
|
import { createModiRelatedCheckers } from '../store/modi';
|
||||||
import { SyncContext } from '../store/SyncRowStore';
|
import { SyncContext } from '../store/SyncRowStore';
|
||||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefDict } from '../types';
|
import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefDict } from '../types';
|
||||||
|
|
@ -8,6 +8,7 @@ import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefD
|
||||||
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){
|
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){
|
||||||
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
||||||
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
|
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
|
||||||
|
// checkers.push(...createRemoveCheckers<ED, Cxt>(schema));
|
||||||
if (authDict) {
|
if (authDict) {
|
||||||
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
|
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5222,6 +5222,26 @@ function constructAttributes(entity: string): ts.PropertyAssignment[] {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
// 如果是entity,在这里处理一下ref
|
||||||
|
if (ts.isIdentifier(name) && name.text === 'entity') {
|
||||||
|
const mtoRelations = ReversePointerRelations[entity];
|
||||||
|
if (mtoRelations) {
|
||||||
|
const mtoEntities = mtoRelations.map(
|
||||||
|
ele => firstLetterLowerCase(ele)
|
||||||
|
);
|
||||||
|
attrAssignments.push(
|
||||||
|
factory.createPropertyAssignment(
|
||||||
|
factory.createIdentifier("ref"),
|
||||||
|
factory.createArrayLiteralExpression(
|
||||||
|
mtoEntities.map(
|
||||||
|
ele => factory.createStringLiteral(ele)
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'Text':
|
case 'Text':
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { EntityDict, OperateOption, SelectOption, TriggerDataAttribute, TriggerT
|
||||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||||
import { Logger } from "../types/Logger";
|
import { Logger } from "../types/Logger";
|
||||||
import { Checker, CheckerType, LogicalChecker, RelationChecker } from '../types/Auth';
|
import { Checker, CheckerType, LogicalChecker, RelationChecker } from '../types/Auth';
|
||||||
import { Trigger, CreateTriggerCrossTxn, CreateTrigger, CreateTriggerInTxn, SelectTriggerAfter, UpdateTrigger } from "../types/Trigger";
|
import { Trigger, CreateTriggerCrossTxn, CreateTrigger, CreateTriggerInTxn, SelectTriggerAfter, UpdateTrigger, TRIGGER_DEFAULT_PRIORITY, CHECKER_DEFAULT_PRIORITY, DATA_CHECKER_DEFAULT_PRIORITY, TRIGGER_MAX_PRIORITY, TRIGGER_MIN_PRIORITY } from "../types/Trigger";
|
||||||
import { AsyncContext } from './AsyncRowStore';
|
import { AsyncContext } from './AsyncRowStore';
|
||||||
import { SyncContext } from './SyncRowStore';
|
import { SyncContext } from './SyncRowStore';
|
||||||
import { translateCheckerInAsyncContext } from './checker';
|
import { translateCheckerInAsyncContext } from './checker';
|
||||||
|
|
@ -50,10 +50,11 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
||||||
const { entity, action, type, conditionalFilter } = checker;
|
const { entity, action, type, conditionalFilter } = checker;
|
||||||
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
|
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
|
||||||
const { fn, when } = translateCheckerInAsyncContext(checker);
|
const { fn, when } = translateCheckerInAsyncContext(checker);
|
||||||
|
const priority = type === 'data' ? DATA_CHECKER_DEFAULT_PRIORITY : CHECKER_DEFAULT_PRIORITY; // checker的默认优先级最低(前面的trigger可能会赋上一些相应的值)
|
||||||
const trigger = {
|
const trigger = {
|
||||||
checkerType: type,
|
checkerType: type,
|
||||||
name: triggerName,
|
name: triggerName,
|
||||||
priority: checker.priority || 20, // checker的默认优先级稍高
|
priority: checker.priority || priority,
|
||||||
entity,
|
entity,
|
||||||
action: action as 'update',
|
action: action as 'update',
|
||||||
fn,
|
fn,
|
||||||
|
|
@ -77,7 +78,10 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
||||||
throw new Error(`不可有同名的触发器「${trigger.name}」`);
|
throw new Error(`不可有同名的触发器「${trigger.name}」`);
|
||||||
}
|
}
|
||||||
if (typeof trigger.priority !== 'number') {
|
if (typeof trigger.priority !== 'number') {
|
||||||
trigger.priority = 10; // 默认值
|
trigger.priority = TRIGGER_DEFAULT_PRIORITY; // 默认值
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(trigger.priority <= TRIGGER_MAX_PRIORITY && trigger.priority >= TRIGGER_MIN_PRIORITY, `trigger「${trigger.name}」的优先级定义越界,应该在${TRIGGER_MIN_PRIORITY}到${TRIGGER_MAX_PRIORITY}之间`);
|
||||||
}
|
}
|
||||||
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
|
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
|
||||||
assert(typeof trigger.action === 'string' && trigger.action !== 'create'
|
assert(typeof trigger.action === 'string' && trigger.action !== 'create'
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ import { addFilterSegment, checkFilterContains, combineFilters } from "../store/
|
||||||
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
|
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
|
||||||
import {
|
import {
|
||||||
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
|
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
|
||||||
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy
|
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||||
import { AsyncContext } from "./AsyncRowStore";
|
import { AsyncContext } from "./AsyncRowStore";
|
||||||
import { getFullProjection } from './actionDef';
|
import { getFullProjection } from './actionDef';
|
||||||
import { SyncContext } from './SyncRowStore';
|
import { SyncContext } from './SyncRowStore';
|
||||||
import { firstLetterUpperCase } from '../utils/string';
|
import { firstLetterUpperCase } from '../utils/string';
|
||||||
import { uniq, difference } from '../utils/lodash';
|
import { intersection, uniq, difference } from '../utils/lodash';
|
||||||
import { judgeRelation } from './relation';
|
import { judgeRelation } from './relation';
|
||||||
|
|
||||||
export function translateCheckerInAsyncContext<
|
export function translateCheckerInAsyncContext<
|
||||||
|
|
@ -371,7 +371,12 @@ function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||||
return (userId) => filterMaker(userId);
|
return (userId) => filterMaker(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据权限定义,创建出相应的checker
|
||||||
|
* @param schema
|
||||||
|
* @param authDict
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
|
export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
|
||||||
schema: StorageSchema<ED>,
|
schema: StorageSchema<ED>,
|
||||||
authDict: AuthDefDict<ED>) {
|
authDict: AuthDefDict<ED>) {
|
||||||
|
|
@ -478,3 +483,179 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
|
|
||||||
return checkers;
|
return checkers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对对象的删除,检查其是否会产生其他行上的空指针,不允许这种情况的出现
|
||||||
|
* @param schema
|
||||||
|
* @returns
|
||||||
|
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||||
|
*/
|
||||||
|
export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>) {
|
||||||
|
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
||||||
|
|
||||||
|
// 先建立所有的一对多的关系
|
||||||
|
const OneToManyMatrix: Partial<Record<keyof ED, Array<[keyof ED, string]>>> = {};
|
||||||
|
const OneToManyOnEntityMatrix: Partial<Record<keyof ED, Array<keyof ED>>> = {};
|
||||||
|
|
||||||
|
const addToMto = (e: keyof ED, f: keyof ED, attr: string) => {
|
||||||
|
if (OneToManyMatrix[f]) {
|
||||||
|
OneToManyMatrix[f]?.push([e, attr]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
OneToManyMatrix[f] = [[e, attr]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToMtoEntity = (e: keyof ED, fs: Array<keyof ED>) => {
|
||||||
|
for (const f of fs) {
|
||||||
|
if (!OneToManyOnEntityMatrix[f]) {
|
||||||
|
OneToManyOnEntityMatrix[f] = [e];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
OneToManyOnEntityMatrix[f]?.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const entity in schema) {
|
||||||
|
if (['operEntity'].includes(entity)) {
|
||||||
|
continue; // OperEntity会指向每一个对象,不必处理
|
||||||
|
}
|
||||||
|
const { attributes } = schema[entity];
|
||||||
|
for (const attr in attributes) {
|
||||||
|
if (attributes[attr].type === 'ref') {
|
||||||
|
addToMto(entity, attributes[attr].ref as keyof ED, attr);
|
||||||
|
}
|
||||||
|
else if (attr === 'entity') {
|
||||||
|
if (attributes[attr].ref) {
|
||||||
|
addToMtoEntity(entity, attributes[attr].ref as Array<keyof ED>);
|
||||||
|
}
|
||||||
|
else if (process.env.NODE_ENV === 'development') {
|
||||||
|
console.warn(`${entity}的entity反指指针找不到有效的对象`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当删除一时,要确认多上面没有指向一的数据
|
||||||
|
const entities = intersection(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
||||||
|
for (const entity of entities) {
|
||||||
|
checkers.push({
|
||||||
|
entity: entity as keyof ED,
|
||||||
|
action: 'remove',
|
||||||
|
type: 'logical',
|
||||||
|
checker: (operation, context, option) => {
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
if (OneToManyMatrix[entity]) {
|
||||||
|
for (const otm of OneToManyMatrix[entity]!) {
|
||||||
|
const [e, attr] = otm;
|
||||||
|
const proj = {
|
||||||
|
id: 1,
|
||||||
|
[attr]: 1,
|
||||||
|
};
|
||||||
|
const filter = operation.filter && {
|
||||||
|
[attr.slice(0, attr.length -2)]: operation.filter
|
||||||
|
}
|
||||||
|
const result = context.select(e, {
|
||||||
|
data: proj,
|
||||||
|
filter,
|
||||||
|
indexFrom: 0,
|
||||||
|
count: 1
|
||||||
|
}, { dontCollect: true });
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
promises.push(
|
||||||
|
result.then(
|
||||||
|
([row]) => {
|
||||||
|
if (row) {
|
||||||
|
const record = {
|
||||||
|
a: 's',
|
||||||
|
d: {
|
||||||
|
[e]: {
|
||||||
|
[row.id!]: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as SelectOpResult<ED>;
|
||||||
|
throw new OakRowInconsistencyException(record, `您无法删除存在有效数据「${e as string}」关联的行`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const [row] = result;
|
||||||
|
if (row) {
|
||||||
|
const record = {
|
||||||
|
a: 's',
|
||||||
|
d: {
|
||||||
|
[e]: {
|
||||||
|
[row.id!]: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as SelectOpResult<ED>;
|
||||||
|
throw new OakRowInconsistencyException(record, `您无法删除存在有效数据「${e as string}」关联的行`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (OneToManyOnEntityMatrix[entity]) {
|
||||||
|
for (const otm of OneToManyOnEntityMatrix[entity]!) {
|
||||||
|
const proj = {
|
||||||
|
id: 1,
|
||||||
|
entity: 1,
|
||||||
|
entityId: 1,
|
||||||
|
};
|
||||||
|
const filter = operation.filter && {
|
||||||
|
[entity]: operation.filter
|
||||||
|
}
|
||||||
|
const result = context.select(otm, {
|
||||||
|
data: proj,
|
||||||
|
filter,
|
||||||
|
indexFrom: 0,
|
||||||
|
count: 1
|
||||||
|
}, { dontCollect: true });
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
promises.push(
|
||||||
|
result.then(
|
||||||
|
([row]) => {
|
||||||
|
if (row) {
|
||||||
|
const record = {
|
||||||
|
a: 's',
|
||||||
|
d: {
|
||||||
|
[otm]: {
|
||||||
|
[row.id!]: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as SelectOpResult<ED>;
|
||||||
|
throw new OakRowInconsistencyException(record, `您无法删除存在有效数据「${otm as string}」关联的行`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const [row] = result;
|
||||||
|
if (row) {
|
||||||
|
const record = {
|
||||||
|
a: 's',
|
||||||
|
d: {
|
||||||
|
[otm]: {
|
||||||
|
[row.id!]: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as SelectOpResult<ED>;
|
||||||
|
throw new OakRowInconsistencyException(record, `您无法删除存在有效数据「${otm as string}」关联的行`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (promises.length > 0) {
|
||||||
|
return Promise.all(promises).then(
|
||||||
|
() => undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkers;
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends A
|
||||||
type: 'data';
|
type: 'data';
|
||||||
entity: T;
|
entity: T;
|
||||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void | Promise<void>;
|
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||||
);
|
);
|
||||||
|
|
@ -62,7 +62,7 @@ export type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extend
|
||||||
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
option: OperateOption | SelectOption
|
option: OperateOption | SelectOption
|
||||||
) => void | Promise<void>;
|
) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cx
|
||||||
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
option: OperateOption | SelectOption
|
option: OperateOption | SelectOption
|
||||||
) => void | Promise<void>;
|
) => any | Promise<any>;
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export interface Index<SH extends EntityShape> {
|
||||||
export interface Attribute {
|
export interface Attribute {
|
||||||
type: DataType | Ref;
|
type: DataType | Ref;
|
||||||
params?: DataTypeParams;
|
params?: DataTypeParams;
|
||||||
ref?: string;
|
ref?: string | string[];
|
||||||
onRefDelete?: 'delete' | 'setNull' | 'ignore';
|
onRefDelete?: 'delete' | 'setNull' | 'ignore';
|
||||||
default?: string | number | boolean;
|
default?: string | number | boolean;
|
||||||
notNull?: boolean;
|
notNull?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@ import { SyncContext } from "../store/SyncRowStore";
|
||||||
import { EntityDict, OperateOption } from "../types/Entity";
|
import { EntityDict, OperateOption } from "../types/Entity";
|
||||||
import { EntityShape } from "../types/Entity";
|
import { EntityShape } from "../types/Entity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级越小,越早执行。定义在1~99之间
|
||||||
|
*/
|
||||||
|
export const TRIGGER_DEFAULT_PRIORITY = 50;
|
||||||
|
export const TRIGGER_MIN_PRIORITY = 1;
|
||||||
|
export const TRIGGER_MAX_PRIORITY = 99;
|
||||||
|
export const DATA_CHECKER_DEFAULT_PRIORITY = 60;
|
||||||
|
export const CHECKER_DEFAULT_PRIORITY = 99;
|
||||||
|
|
||||||
interface TriggerBase<ED extends EntityDict, T extends keyof ED> {
|
interface TriggerBase<ED extends EntityDict, T extends keyof ED> {
|
||||||
checkerType?: CheckerType;
|
checkerType?: CheckerType;
|
||||||
entity: T;
|
entity: T;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue