大改了relationAuth的实现,以及cascadeStore的结构性重构
This commit is contained in:
parent
c044a7fcfe
commit
79dc2cfdbb
|
|
@ -4,9 +4,10 @@ export declare const ENTITY_PATH_IN_OAK_GENERAL_BUSINESS: () => string;
|
|||
export declare const ENTITY_PATH_IN_OAK_DOMAIN: () => string;
|
||||
export declare const TYPE_PATH_IN_OAK_DOMAIN: (level?: number) => string;
|
||||
export declare const ACTION_CONSTANT_IN_OAK_DOMAIN: (level?: number) => string;
|
||||
export declare const RESERVED_ENTITIES: string[];
|
||||
export declare const RESERVED_ENTITY_NAMES: string[];
|
||||
export declare const ENTITY_NAME_MAX_LENGTH = 32;
|
||||
export declare const STRING_LITERAL_MAX_LENGTH = 24;
|
||||
export declare const NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
|
||||
export declare const NUMERICAL_LITERL_DEFAULT_SCALE = 2;
|
||||
export declare const INT_LITERL_DEFAULT_WIDTH = 4;
|
||||
export declare const SYSTEM_RESERVE_ENTITIES: string[];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.ENTITY_NAME_MAX_LENGTH = exports.RESERVED_ENTITIES = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0;
|
||||
exports.SYSTEM_RESERVE_ENTITIES = exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.ENTITY_NAME_MAX_LENGTH = exports.RESERVED_ENTITY_NAMES = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0;
|
||||
exports.LIB_OAK_DOMAIN = 'oak-domain';
|
||||
var LIB_OAK_GENERAL_BUSINESS = 'oak-general-business';
|
||||
var LIB_PATH = function () { return 'lib'; };
|
||||
|
|
@ -31,10 +31,12 @@ var ACTION_CONSTANT_IN_OAK_DOMAIN = function (level) {
|
|||
};
|
||||
exports.ACTION_CONSTANT_IN_OAK_DOMAIN = ACTION_CONSTANT_IN_OAK_DOMAIN;
|
||||
// export const OUTPUT_PATH = 'app-domain/entities';
|
||||
exports.RESERVED_ENTITIES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
|
||||
exports.RESERVED_ENTITY_NAMES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
|
||||
'Locale', 'Projection', 'Data'];
|
||||
exports.ENTITY_NAME_MAX_LENGTH = 32;
|
||||
exports.STRING_LITERAL_MAX_LENGTH = 24;
|
||||
exports.NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
|
||||
exports.NUMERICAL_LITERL_DEFAULT_SCALE = 2;
|
||||
exports.INT_LITERL_DEFAULT_WIDTH = 4;
|
||||
// 暂放在这儿
|
||||
exports.SYSTEM_RESERVE_ENTITIES = ['user', 'relation', 'oper', 'operEntity', 'modi', 'modiEntity', 'userRelation', 'actionAuth', 'relationAuth', 'relation'];
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ function dealWithActions(moduleName, filename, node, program, sourceFile, hasRel
|
|||
return ele.typeName.text;
|
||||
}
|
||||
}).filter(function (ele) { return !!ele; });
|
||||
(0, assert_1.default)((0, lodash_1.intersection)(actionNames, env_1.RESERVED_ENTITIES).length === 0, "".concat(filename, "\u4E2D\u7684Action\u547D\u540D\u4E0D\u80FD\u662F\u300C").concat(RESERVED_ACTION_NAMES.join(','), "\u300D\u4E4B\u4E00"));
|
||||
(0, assert_1.default)((0, lodash_1.intersection)(actionNames, RESERVED_ACTION_NAMES).length === 0, "".concat(filename, "\u4E2D\u7684Action\u547D\u540D\u4E0D\u80FD\u662F\u300C").concat(RESERVED_ACTION_NAMES.join(','), "\u300D\u4E4B\u4E00"));
|
||||
node.types.forEach(function (ele) {
|
||||
if (ts.isTypeReferenceNode(ele)) {
|
||||
actionTexts.push.apply(actionTexts, tslib_1.__spreadArray([], tslib_1.__read(getStringTextFromUnionStringLiterals(moduleName, filename, ele, program)), false));
|
||||
|
|
@ -3798,7 +3798,7 @@ function analyzeEntities(inputDir, relativePath) {
|
|||
var files = (0, fs_1.readdirSync)(inputDir);
|
||||
var fullFilenames = files.map(function (ele) {
|
||||
var entity = ele.slice(0, ele.indexOf('.'));
|
||||
if (env_1.RESERVED_ENTITIES.includes(entity) || env_1.RESERVED_ENTITIES.find(function (ele2) { return entity.startsWith(ele2); })) {
|
||||
if (env_1.RESERVED_ENTITY_NAMES.includes(entity) || env_1.RESERVED_ENTITY_NAMES.find(function (ele2) { return entity.startsWith(ele2); })) {
|
||||
throw new Error("".concat(ele, "\u662F\u7CFB\u7EDF\u4FDD\u7559\u5B57\uFF0C\u8BF7\u52FF\u4F7F\u7528\u5176\u5F53\u5BF9\u8C61\u540D\u6216\u5BF9\u8C61\u540D\u524D\u7F00"));
|
||||
}
|
||||
return "".concat(inputDir, "/").concat(ele);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
|||
private selectionRewriters;
|
||||
private operationRewriters;
|
||||
private reinforceSelection;
|
||||
private reinforceSelectionInner;
|
||||
private reinforceOperation;
|
||||
registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
|
||||
registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
|
||||
registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED>>): void;
|
||||
registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED>>): void;
|
||||
protected abstract selectAbjointRow<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']>[];
|
||||
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): number;
|
||||
protected abstract selectAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]['Schema']>[]>;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ var lodash_1 = require("../utils/lodash");
|
|||
var AsyncRowStore_1 = require("./AsyncRowStore");
|
||||
var filter_2 = require("./filter");
|
||||
var uuid_1 = require("../utils/uuid");
|
||||
var env_1 = require("../compiler/env");
|
||||
/**这个用来处理级联的select和update,对不同能力的 */
|
||||
var CascadeStore = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(CascadeStore, _super);
|
||||
|
|
@ -21,7 +22,47 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
_this.operationRewriters = [];
|
||||
return _this;
|
||||
}
|
||||
CascadeStore.prototype.reinforceSelection = function (entity, selection) {
|
||||
CascadeStore.prototype.reinforceSelection = function (entity, selection, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var noRelationDestEntities, rewriterPromises;
|
||||
var _this = this;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
noRelationDestEntities = [];
|
||||
this.reinforceSelectionInner(entity, selection, context, noRelationDestEntities);
|
||||
rewriterPromises = this.selectionRewriters.map(function (ele) { return ele(_this.getSchema(), entity, selection, context); });
|
||||
// 这个设计每次都要取actionAuth的数据,感觉不是很优雅。by Xc 20230722
|
||||
if (noRelationDestEntities.length > 0 && !option.dontCollect) {
|
||||
rewriterPromises.push(context.select('actionAuth', {
|
||||
data: {
|
||||
id: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
},
|
||||
filter: {
|
||||
relationId: {
|
||||
$exists: false,
|
||||
},
|
||||
destEntity: {
|
||||
$in: noRelationDestEntities,
|
||||
},
|
||||
},
|
||||
}, {}));
|
||||
}
|
||||
if (!(rewriterPromises.length > 0)) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, Promise.all(rewriterPromises)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
_a.label = 2;
|
||||
case 2: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
CascadeStore.prototype.reinforceSelectionInner = function (entity, selection, context, noRelationDestEntities) {
|
||||
var _this = this;
|
||||
var filter = selection.filter, data = selection.data, sorter = selection.sorter;
|
||||
var checkNode = function (projectionNode, attrs) {
|
||||
|
|
@ -143,91 +184,131 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
var toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
|
||||
var projectionNodeDict = {};
|
||||
var checkProjectionNode = function (entity2, projectionNode) {
|
||||
var checkProjectionNode = function (entity2, projectionNode) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var necessaryAttrs, attr, exprResult, nodeName, rel, data_1, userId;
|
||||
var _a, _b, _c;
|
||||
var necessaryAttrs = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
||||
for (var attr in projectionNode) {
|
||||
if (attr === '#id') {
|
||||
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
|
||||
Object.assign(projectionNodeDict, (_a = {},
|
||||
_a[projectionNode[attr]] = projectionNode,
|
||||
_a));
|
||||
if (toBeAssignNode2[projectionNode[attr]]) {
|
||||
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
||||
var exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
|
||||
for (var nodeName in exprResult) {
|
||||
if (nodeName === '#current') {
|
||||
checkNode(projectionNode, exprResult[nodeName]);
|
||||
}
|
||||
else if (projectionNodeDict[nodeName]) {
|
||||
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
|
||||
}
|
||||
else {
|
||||
if (toBeAssignNode2[nodeName]) {
|
||||
(_b = toBeAssignNode2[nodeName]).push.apply(_b, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
|
||||
}
|
||||
else {
|
||||
Object.assign(toBeAssignNode2, (_c = {},
|
||||
_c[nodeName] = exprResult[nodeName],
|
||||
_c));
|
||||
}
|
||||
}
|
||||
return tslib_1.__generator(this, function (_d) {
|
||||
necessaryAttrs = ['id', '$$createAt$$'];
|
||||
for (attr in projectionNode) {
|
||||
if (attr === '#id') {
|
||||
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
|
||||
Object.assign(projectionNodeDict, (_a = {},
|
||||
_a[projectionNode[attr]] = projectionNode,
|
||||
_a));
|
||||
if (toBeAssignNode2[projectionNode[attr]]) {
|
||||
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var rel = (0, relation_1.judgeRelation)(_this.getSchema(), entity2, attr);
|
||||
if (rel === 1) {
|
||||
necessaryAttrs.push(attr);
|
||||
}
|
||||
else if (rel === 2) {
|
||||
// entity/entityId反指
|
||||
necessaryAttrs.push('entity', 'entityId');
|
||||
checkProjectionNode(attr, projectionNode[attr]);
|
||||
}
|
||||
else if (typeof rel === 'string') {
|
||||
necessaryAttrs.push("".concat(attr, "Id"));
|
||||
checkProjectionNode(rel, projectionNode[attr]);
|
||||
}
|
||||
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
|
||||
var data_1 = projectionNode[attr].data;
|
||||
if (rel[1]) {
|
||||
checkNode(data_1, [rel[1]]);
|
||||
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
||||
exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
|
||||
for (nodeName in exprResult) {
|
||||
if (nodeName === '#current') {
|
||||
checkNode(projectionNode, exprResult[nodeName]);
|
||||
}
|
||||
else if (projectionNodeDict[nodeName]) {
|
||||
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
|
||||
}
|
||||
else {
|
||||
if (toBeAssignNode2[nodeName]) {
|
||||
(_b = toBeAssignNode2[nodeName]).push.apply(_b, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
|
||||
}
|
||||
else {
|
||||
Object.assign(toBeAssignNode2, (_c = {},
|
||||
_c[nodeName] = exprResult[nodeName],
|
||||
_c));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
checkNode(data_1, ['entity', 'entityId']);
|
||||
}
|
||||
else {
|
||||
rel = (0, relation_1.judgeRelation)(this.getSchema(), entity2, attr);
|
||||
if (rel === 1) {
|
||||
necessaryAttrs.push(attr);
|
||||
}
|
||||
else if (rel === 2) {
|
||||
// entity/entityId反指
|
||||
necessaryAttrs.push('entity', 'entityId');
|
||||
checkProjectionNode(attr, projectionNode[attr]);
|
||||
}
|
||||
else if (typeof rel === 'string') {
|
||||
necessaryAttrs.push("".concat(attr, "Id"));
|
||||
checkProjectionNode(rel, projectionNode[attr]);
|
||||
}
|
||||
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
|
||||
data_1 = projectionNode[attr].data;
|
||||
if (rel[1]) {
|
||||
checkNode(data_1, [rel[1]]);
|
||||
}
|
||||
else {
|
||||
checkNode(data_1, ['entity', 'entityId']);
|
||||
}
|
||||
this.reinforceSelectionInner(rel[0], projectionNode[attr], context, noRelationDestEntities);
|
||||
}
|
||||
_this.reinforceSelection(rel[0], projectionNode[attr]);
|
||||
}
|
||||
}
|
||||
checkNode(projectionNode, necessaryAttrs);
|
||||
}
|
||||
checkNode(projectionNode, necessaryAttrs);
|
||||
}
|
||||
// 如果对象中指向一对多的Modi,此时加上指向Modi的projection
|
||||
if (_this.getSchema()[entity2].toModi) {
|
||||
Object.assign(projectionNode, {
|
||||
modi$entity: {
|
||||
$entity: 'modi',
|
||||
data: {
|
||||
id: 1,
|
||||
targetEntity: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
action: 1,
|
||||
iState: 1,
|
||||
data: 1,
|
||||
filter: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'active',
|
||||
},
|
||||
// 如果对象中指向一对多的Modi,此时加上指向Modi的projection
|
||||
if (this.getSchema()[entity2].toModi) {
|
||||
Object.assign(projectionNode, {
|
||||
modi$entity: {
|
||||
$entity: 'modi',
|
||||
data: {
|
||||
id: 1,
|
||||
targetEntity: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
action: 1,
|
||||
iState: 1,
|
||||
data: 1,
|
||||
filter: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'active',
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
userId = context.getCurrentUserId(true);
|
||||
if (userId && !env_1.SYSTEM_RESERVE_ENTITIES.includes(entity2)) {
|
||||
if (this.getSchema()[entity2].relation && !projectionNode.userRelation$entity) {
|
||||
Object.assign(projectionNode, {
|
||||
userRelation$entity: {
|
||||
$entity: 'userRelation',
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
userId: 1,
|
||||
relationId: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
display: 1,
|
||||
actionAuth$relation: {
|
||||
$entity: 'actionAuth',
|
||||
data: {
|
||||
id: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
userId: userId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
noRelationDestEntities.push(entity2);
|
||||
}
|
||||
return [2 /*return*/];
|
||||
});
|
||||
}); };
|
||||
checkProjectionNode(entity, data);
|
||||
if (!sorter && relevantIds.length === 0) {
|
||||
// 如果没有sorter,就给予一个按createAt逆序的sorter
|
||||
|
|
@ -245,11 +326,19 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
$$createAt$$: 1,
|
||||
});
|
||||
}
|
||||
this.selectionRewriters.forEach(function (ele) { return ele(_this.getSchema(), entity, selection); });
|
||||
};
|
||||
CascadeStore.prototype.reinforceOperation = function (entity, operation) {
|
||||
var _this = this;
|
||||
this.operationRewriters.forEach(function (ele) { return ele(_this.getSchema(), entity, operation); });
|
||||
CascadeStore.prototype.reinforceOperation = function (entity, operation, context) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var _this = this;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, Promise.all(this.operationRewriters.map(function (ele) { return ele(_this.getSchema(), entity, operation, context); }))];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
CascadeStore.prototype.registerOperationRewriter = function (rewriter) {
|
||||
this.operationRewriters.push(rewriter);
|
||||
|
|
@ -1651,7 +1740,6 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
return tslib_1.__generator(this, function (_h) {
|
||||
switch (_h.label) {
|
||||
case 0:
|
||||
this.reinforceOperation(entity, operation);
|
||||
action = operation.action, data = operation.data, filter = operation.filter, id = operation.id;
|
||||
wholeBeforeFns = [];
|
||||
wholeAfterFns = [];
|
||||
|
|
@ -1750,7 +1838,6 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
});
|
||||
};
|
||||
CascadeStore.prototype.cascadeSelect = function (entity, selection, context, option) {
|
||||
this.reinforceSelection(entity, selection);
|
||||
var data = selection.data, filter = selection.filter, indexFrom = selection.indexFrom, count = selection.count, sorter = selection.sorter;
|
||||
var _a = this.destructCascadeSelect(entity, data, context, this.cascadeSelect, this.aggregateSync, option), projection = _a.projection, cascadeSelectionFns = _a.cascadeSelectionFns;
|
||||
var rows = this.selectAbjointRow(entity, {
|
||||
|
|
@ -1926,8 +2013,12 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
CascadeStore.prototype.selectAsync = function (entity, selection, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
this.reinforceSelection(entity, selection);
|
||||
return [2 /*return*/, this.cascadeSelectAsync(entity, selection, context, option)];
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, this.reinforceSelection(entity, selection, context, option)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, this.cascadeSelectAsync(entity, selection, context, option)];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -1939,8 +2030,16 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
return this.cascadeUpdate(entity, operation, context, option);
|
||||
};
|
||||
CascadeStore.prototype.operateAsync = function (entity, operation, context, option) {
|
||||
this.reinforceOperation(entity, operation);
|
||||
return this.operateAsync(entity, operation, context, option);
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, this.reinforceOperation(entity, operation, context)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, this.cascadeUpdateAsync(entity, operation, context, option)];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
return CascadeStore;
|
||||
}(RowStore_1.RowStore));
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ var relation_1 = require("./relation");
|
|||
var SyncRowStore_1 = require("./SyncRowStore");
|
||||
var action_1 = require("../actions/action");
|
||||
var lodash_1 = require("../utils/lodash");
|
||||
var env_1 = require("../compiler/env");
|
||||
var RelationAuth = /** @class */ (function () {
|
||||
function RelationAuth(schema, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities) {
|
||||
this.actionCascadePathGraph = actionCascadePathGraph;
|
||||
|
|
@ -1911,7 +1912,7 @@ var RelationAuth = /** @class */ (function () {
|
|||
}
|
||||
}
|
||||
};
|
||||
RelationAuth.SPECIAL_ENTITIES = ['user', 'relation', 'oper', 'operEntity', 'modi', 'modiEntity', 'userRelation', 'actionAuth', 'relationAuth', 'relation'];
|
||||
RelationAuth.SPECIAL_ENTITIES = env_1.SYSTEM_RESERVE_ENTITIES;
|
||||
return RelationAuth;
|
||||
}());
|
||||
exports.RelationAuth = RelationAuth;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { OperationResult, EntityDict } from './Entity';
|
||||
import { StorageSchema } from './Storage';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
export declare type TxnOption = {
|
||||
isolationLevel: 'repeatable read' | 'serializable';
|
||||
};
|
||||
export declare type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
|
||||
export declare type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
|
||||
export declare type SelectionRewriter<ED extends EntityDict, Cxt extends AsyncContext<ED>> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection'], context: Cxt) => Promise<void>;
|
||||
export declare type OperationRewriter<ED extends EntityDict, Cxt extends AsyncContext<ED>> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation'], context: Cxt) => Promise<void>;
|
||||
export declare abstract class RowStore<ED extends EntityDict> {
|
||||
protected storageSchema: StorageSchema<ED>;
|
||||
constructor(storageSchema: StorageSchema<ED>);
|
||||
abstract registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
|
||||
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
|
||||
abstract registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED>>): void;
|
||||
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED>>): void;
|
||||
getSchema(): StorageSchema<ED>;
|
||||
mergeOperationResult(result: OperationResult<ED>, toBeMerged: OperationResult<ED>): void;
|
||||
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,13 @@ export const ACTION_CONSTANT_IN_OAK_DOMAIN = (level = 2) => {
|
|||
|
||||
// export const OUTPUT_PATH = 'app-domain/entities';
|
||||
|
||||
export const RESERVED_ENTITIES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
|
||||
export const RESERVED_ENTITY_NAMES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
|
||||
'Locale', 'Projection', 'Data'];
|
||||
export const ENTITY_NAME_MAX_LENGTH = 32;
|
||||
export const STRING_LITERAL_MAX_LENGTH = 24;
|
||||
export const NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
|
||||
export const NUMERICAL_LITERL_DEFAULT_SCALE = 2;
|
||||
export const INT_LITERL_DEFAULT_WIDTH = 4;
|
||||
export const INT_LITERL_DEFAULT_WIDTH = 4;
|
||||
|
||||
// 暂放在这儿
|
||||
export const SYSTEM_RESERVE_ENTITIES = ['user', 'relation', 'oper', 'operEntity', 'modi', 'modiEntity', 'userRelation', 'actionAuth', 'relationAuth', 'relation'];
|
||||
|
|
@ -8,7 +8,7 @@ const { factory } = ts;
|
|||
import {
|
||||
ACTION_CONSTANT_IN_OAK_DOMAIN,
|
||||
TYPE_PATH_IN_OAK_DOMAIN,
|
||||
RESERVED_ENTITIES,
|
||||
RESERVED_ENTITY_NAMES,
|
||||
STRING_LITERAL_MAX_LENGTH,
|
||||
NUMERICAL_LITERL_DEFAULT_PRECISION,
|
||||
NUMERICAL_LITERL_DEFAULT_SCALE,
|
||||
|
|
@ -362,7 +362,7 @@ function dealWithActions(moduleName: string, filename: string, node: ts.TypeNode
|
|||
).filter(
|
||||
ele => !!ele
|
||||
);
|
||||
assert(intersection(actionNames, RESERVED_ENTITIES).length === 0,
|
||||
assert(intersection(actionNames, RESERVED_ACTION_NAMES).length === 0,
|
||||
`${filename}中的Action命名不能是「${RESERVED_ACTION_NAMES.join(',')}」之一`);
|
||||
|
||||
node.types.forEach(
|
||||
|
|
@ -6852,7 +6852,7 @@ export function analyzeEntities(inputDir: string, relativePath?: string) {
|
|||
const fullFilenames = files.map(
|
||||
ele => {
|
||||
const entity = ele.slice(0, ele.indexOf('.'))
|
||||
if (RESERVED_ENTITIES.includes(entity) || RESERVED_ENTITIES.find(
|
||||
if (RESERVED_ENTITY_NAMES.includes(entity) || RESERVED_ENTITY_NAMES.find(
|
||||
ele2 => entity.startsWith(ele2)
|
||||
)) {
|
||||
throw new Error(`${ele}是系统保留字,请勿使用其当对象名或对象名前缀`);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { getRelevantIds } from "./filter";
|
|||
import { CreateSingleOperation as CreateSingleOperOperation } from '../base-app-domain/Oper/Schema';
|
||||
import { CreateOperation as CreateModiOperation, UpdateOperation as UpdateModiOperation } from '../base-app-domain/Modi/Schema';
|
||||
import { generateNewIdAsync } from "../utils/uuid";
|
||||
import { SYSTEM_RESERVE_ENTITIES } from "../compiler/env";
|
||||
|
||||
/**这个用来处理级联的select和update,对不同能力的 */
|
||||
export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> extends RowStore<ED> {
|
||||
|
|
@ -25,10 +26,50 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
protected abstract supportManyToOneJoin(): boolean;
|
||||
protected abstract supportMultipleCreate(): boolean;
|
||||
|
||||
private selectionRewriters: SelectionRewriter<any>[] = [];
|
||||
private operationRewriters: OperationRewriter<any>[] = [];
|
||||
private selectionRewriters: SelectionRewriter<ED, AsyncContext<ED>>[] = [];
|
||||
private operationRewriters: OperationRewriter<ED, AsyncContext<ED>>[] = [];
|
||||
|
||||
private reinforceSelection(entity: keyof ED, selection: ED[keyof ED]['Selection']) {
|
||||
private async reinforceSelection<Cxt extends AsyncContext<ED>, OP extends SelectOption>(entity: keyof ED, selection: ED[keyof ED]['Selection'], context: Cxt, option: OP) {
|
||||
const noRelationDestEntities: string[] = [];
|
||||
this.reinforceSelectionInner(entity, selection, context, noRelationDestEntities);
|
||||
|
||||
const rewriterPromises: Promise<any>[] = this.selectionRewriters.map(
|
||||
ele => ele(this.getSchema(), entity, selection, context)
|
||||
);
|
||||
|
||||
// 这个设计每次都要取actionAuth的数据,感觉不是很优雅。by Xc 20230722
|
||||
if (noRelationDestEntities.length > 0 && !option.dontCollect) {
|
||||
rewriterPromises.push(
|
||||
context.select('actionAuth', {
|
||||
data: {
|
||||
id: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
},
|
||||
filter: {
|
||||
relationId: {
|
||||
$exists: false,
|
||||
},
|
||||
destEntity: {
|
||||
$in: noRelationDestEntities,
|
||||
},
|
||||
},
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
|
||||
if (rewriterPromises.length > 0) {
|
||||
await Promise.all(rewriterPromises);
|
||||
}
|
||||
}
|
||||
|
||||
private reinforceSelectionInner<Cxt extends AsyncContext<ED>, OP extends SelectOption>(
|
||||
entity: keyof ED,
|
||||
selection: ED[keyof ED]['Selection'],
|
||||
context: Cxt,
|
||||
noRelationDestEntities: string[]) {
|
||||
const { filter, data, sorter } = selection;
|
||||
|
||||
const checkNode = (projectionNode: ED[keyof ED]['Selection']['data'], attrs: string[]) => {
|
||||
|
|
@ -148,7 +189,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
|
||||
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
|
||||
const projectionNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
|
||||
const checkProjectionNode = (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
|
||||
const checkProjectionNode = async (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
|
||||
const necessaryAttrs: string[] = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
||||
for (const attr in projectionNode) {
|
||||
if (attr === '#id') {
|
||||
|
|
@ -204,7 +245,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
else {
|
||||
checkNode(data, ['entity', 'entityId']);
|
||||
}
|
||||
this.reinforceSelection(rel[0], projectionNode[attr]);
|
||||
this.reinforceSelectionInner(rel[0], projectionNode[attr], context, noRelationDestEntities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -232,6 +273,46 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 如果对象上有relation关系,在此将本用户相关的relation和actionAuth全部取出
|
||||
// 还要将actionAuth上没有relation关系但destEntity为本对象的行也全部取出,这些是指向userId的可能路径
|
||||
// 放在这里有点怪异,暂先这样
|
||||
const userId = context.getCurrentUserId(true);
|
||||
if (userId && !SYSTEM_RESERVE_ENTITIES.includes(entity2 as string)) {
|
||||
if (this.getSchema()[entity2].relation && !projectionNode.userRelation$entity) {
|
||||
Object.assign(projectionNode, {
|
||||
userRelation$entity: {
|
||||
$entity: 'userRelation',
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
userId: 1,
|
||||
relationId: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
display: 1,
|
||||
actionAuth$relation: {
|
||||
$entity: 'actionAuth',
|
||||
data: {
|
||||
id: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
} as ED['userRelation']['Selection'],
|
||||
});
|
||||
}
|
||||
noRelationDestEntities.push(entity2 as string);
|
||||
}
|
||||
};
|
||||
checkProjectionNode(entity, data);
|
||||
|
||||
|
|
@ -252,22 +333,19 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
});
|
||||
}
|
||||
|
||||
this.selectionRewriters.forEach(
|
||||
ele => ele(this.getSchema(), entity, selection)
|
||||
);
|
||||
}
|
||||
|
||||
private reinforceOperation(entity: keyof ED, operation: ED[keyof ED]['Operation']) {
|
||||
this.operationRewriters.forEach(
|
||||
ele => ele(this.getSchema(), entity, operation)
|
||||
);
|
||||
private async reinforceOperation<Cxt extends AsyncContext<ED>>(entity: keyof ED, operation: ED[keyof ED]['Operation'], context: Cxt) {
|
||||
await Promise.all(this.operationRewriters.map(
|
||||
ele => ele(this.getSchema(), entity, operation, context)
|
||||
));
|
||||
}
|
||||
|
||||
public registerOperationRewriter(rewriter: OperationRewriter<ED>) {
|
||||
public registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED>>) {
|
||||
this.operationRewriters.push(rewriter);
|
||||
}
|
||||
|
||||
public registerSelectionRewriter(rewriter: SelectionRewriter<ED>) {
|
||||
public registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED>>) {
|
||||
this.selectionRewriters.push(rewriter);
|
||||
}
|
||||
|
||||
|
|
@ -1705,7 +1783,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
operation: ED[T]['Operation'],
|
||||
context: Cxt,
|
||||
option: OP): Promise<OperationResult<ED>> {
|
||||
this.reinforceOperation(entity, operation);
|
||||
const { action, data, filter, id } = operation;
|
||||
let opData: any;
|
||||
const wholeBeforeFns: Array<() => Promise<any>> = [];
|
||||
|
|
@ -1768,7 +1845,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
selection: ED[T]['Selection'],
|
||||
context: Cxt,
|
||||
option: OP): Partial<ED[T]['Schema']>[] {
|
||||
this.reinforceSelection(entity, selection);
|
||||
const { data, filter, indexFrom, count, sorter } = selection;
|
||||
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(
|
||||
entity,
|
||||
|
|
@ -1967,7 +2043,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
selection: ED[T]['Selection'],
|
||||
context: Cxt,
|
||||
option: OP): Promise<Partial<ED[T]['Schema']>[]> {
|
||||
this.reinforceSelection(entity, selection);
|
||||
await this.reinforceSelection(entity, selection, context, option);
|
||||
return this.cascadeSelectAsync(entity, selection, context, option);
|
||||
}
|
||||
|
||||
|
|
@ -1989,12 +2065,12 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
return this.cascadeUpdate(entity, operation, context, option);
|
||||
}
|
||||
|
||||
protected operateAsync<T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends OperateOption>(
|
||||
protected async operateAsync<T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends OperateOption>(
|
||||
entity: T,
|
||||
operation: ED[T]['Operation'],
|
||||
context: Cxt,
|
||||
option: OP): Promise<OperationResult<ED>> {
|
||||
this.reinforceOperation(entity, operation);
|
||||
return this.operateAsync(entity, operation, context, option);
|
||||
await this.reinforceOperation(entity, operation, context);
|
||||
return this.cascadeUpdateAsync(entity, operation, context, option);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import { judgeRelation } from "./relation";
|
|||
import { SyncContext } from "./SyncRowStore";
|
||||
import { readOnlyActions } from '../actions/action';
|
||||
import { difference, intersection, set } from '../utils/lodash';
|
||||
import { SYSTEM_RESERVE_ENTITIES } from "../compiler/env";
|
||||
|
||||
|
||||
type OperationTree<ED extends EntityDict & BaseEntityDict> = {
|
||||
|
|
@ -36,7 +37,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
|
|||
private relationCascadePathGraph: AuthCascadePath<ED>[];
|
||||
private authDeduceRelationMap: AuthDeduceRelationMap<ED>;
|
||||
private schema: StorageSchema<ED>;
|
||||
static SPECIAL_ENTITIES = ['user', 'relation', 'oper', 'operEntity', 'modi', 'modiEntity', 'userRelation', 'actionAuth', 'relationAuth', 'relation'];
|
||||
static SPECIAL_ENTITIES = SYSTEM_RESERVE_ENTITIES;
|
||||
/**
|
||||
* 根据当前操作条件,查找到满足actions(overlap关系)的relationId和relativePath
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
import { OperationResult, EntityDict } from './Entity';
|
||||
import { StorageSchema } from './Storage';
|
||||
import { get, set } from '../utils/lodash';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
|
||||
export type TxnOption = {
|
||||
isolationLevel: 'repeatable read' | 'serializable';
|
||||
};
|
||||
|
||||
export type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
|
||||
export type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
|
||||
export type SelectionRewriter<ED extends EntityDict, Cxt extends AsyncContext<ED>> = (
|
||||
schema: StorageSchema<ED>,
|
||||
entity: keyof ED,
|
||||
selection: ED[keyof ED]['Selection'],
|
||||
context: Cxt
|
||||
) => Promise<void>;
|
||||
export type OperationRewriter<ED extends EntityDict, Cxt extends AsyncContext<ED>> = (
|
||||
schema: StorageSchema<ED>,
|
||||
entity: keyof ED,
|
||||
operate: ED[keyof ED]['Operation'],
|
||||
context: Cxt
|
||||
) => Promise<void>;
|
||||
|
||||
|
||||
export abstract class RowStore<ED extends EntityDict> {
|
||||
|
|
@ -17,14 +28,14 @@ export abstract class RowStore<ED extends EntityDict> {
|
|||
this.storageSchema = storageSchema;
|
||||
}
|
||||
|
||||
abstract registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
|
||||
abstract registerOperationRewriter(rewriter: OperationRewriter<ED, AsyncContext<ED>>): void;
|
||||
|
||||
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
|
||||
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED, AsyncContext<ED>>): void;
|
||||
|
||||
getSchema () {
|
||||
getSchema() {
|
||||
return this.storageSchema;
|
||||
}
|
||||
|
||||
|
||||
mergeOperationResult(result: OperationResult<ED>, toBeMerged: OperationResult<ED>) {
|
||||
for (const entity in toBeMerged) {
|
||||
for (const action in toBeMerged[entity]) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue