Merge branch 'release'
This commit is contained in:
commit
0cdec4239b
|
|
@ -38,10 +38,8 @@ exports.desc = {
|
||||||
type: "object"
|
type: "object"
|
||||||
},
|
},
|
||||||
iState: {
|
iState: {
|
||||||
type: "varchar",
|
type: "enum",
|
||||||
params: {
|
enumeration: ["active", "applied", "abandoned"]
|
||||||
length: 24
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionType: "crud",
|
actionType: "crud",
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,8 @@ exports.desc = {
|
||||||
ref: "user"
|
ref: "user"
|
||||||
},
|
},
|
||||||
userState: {
|
userState: {
|
||||||
type: "varchar",
|
type: "enum",
|
||||||
params: {
|
enumeration: ["normal", "merged"]
|
||||||
length: 24
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actionType: "crud",
|
actionType: "crud",
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,13 @@ function checkActionDefNameConsistent(filename, actionDefNode) {
|
||||||
var sName = stateNode.typeName.text.slice(0, stateNode.typeName.text.length - 5);
|
var sName = stateNode.typeName.text.slice(0, stateNode.typeName.text.length - 5);
|
||||||
(0, assert_1.default)(adfName === aName && aName === sName, "\u6587\u4EF6".concat(filename, "\u4E2D\u7684ActionDef").concat(name.text, "\u4E2DActionDef, Action\u548CState\u7684\u547D\u540D\u89C4\u5219\u4E0D\u4E00\u81F4"));
|
(0, assert_1.default)(adfName === aName && aName === sName, "\u6587\u4EF6".concat(filename, "\u4E2D\u7684ActionDef").concat(name.text, "\u4E2DActionDef, Action\u548CState\u7684\u547D\u540D\u89C4\u5219\u4E0D\u4E00\u81F4"));
|
||||||
}
|
}
|
||||||
|
function checkStringLiteralLegal(filename, obj, text, ele) {
|
||||||
|
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "".concat(filename, "\u4E2D\u5F15\u7528\u7684").concat(obj, " ").concat(text, "\u4E2D\u5B58\u5728\u4E0D\u662Fstringliteral\u7684\u7C7B\u578B"));
|
||||||
|
(0, assert_1.default)(!ele.literal.text.includes('$'), "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(text, "\u4E2D\u7684").concat(obj, "\u300C").concat(ele.literal.text, "\u300D\u5305\u542B\u975E\u6CD5\u5B57\u7B26$"));
|
||||||
|
(0, assert_1.default)(ele.literal.text.length > 0, "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(text, "\u4E2D\u7684").concat(obj, "\u300C").concat(ele.literal.text, "\u300D\u957F\u5EA6\u975E\u6CD5"));
|
||||||
|
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "".concat(filename, "\u4E2D\u5F15\u7528\u7684").concat(obj, " ").concat(text, "\u4E2D\u7684\u300C").concat(ele.literal.text, "\u300D\u957F\u5EA6\u8FC7\u957F"));
|
||||||
|
return ele.literal.text;
|
||||||
|
}
|
||||||
function addActionSource(moduleName, name, node) {
|
function addActionSource(moduleName, name, node) {
|
||||||
var _a;
|
var _a;
|
||||||
var ast = ActionAsts[moduleName];
|
var ast = ActionAsts[moduleName];
|
||||||
|
|
@ -199,20 +206,13 @@ function getStringTextFromUnionStringLiterals(moduleName, filename, node, progra
|
||||||
_a[name.text] = 'local',
|
_a[name.text] = 'local',
|
||||||
_a));
|
_a));
|
||||||
}
|
}
|
||||||
var getStringLiteral = function (ele) {
|
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(name.text, "\u4E2D\u5B58\u5728\u4E0D\u662Fstringliteral\u7684\u7C7B\u578B"));
|
|
||||||
(0, assert_1.default)(!ele.literal.text.includes('$'), "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(name.text, "\u4E2D\u7684action\u300C").concat(ele.literal.text, "\u300D\u5305\u542B\u975E\u6CD5\u5B57\u7B26$"));
|
|
||||||
(0, assert_1.default)(ele.literal.text.length > 0, "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(name.text, "\u4E2D\u7684action\u300C").concat(ele.literal.text, "\u300D\u957F\u5EA6\u975E\u6CD5"));
|
|
||||||
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "".concat(filename, "\u4E2D\u5F15\u7528\u7684action").concat(name.text, "\u4E2D\u7684action\u300C").concat(ele.literal.text, "\u300D\u957F\u5EA6\u8FC7\u957F"));
|
|
||||||
return ele.literal.text;
|
|
||||||
};
|
|
||||||
if (ts.isUnionTypeNode(type)) {
|
if (ts.isUnionTypeNode(type)) {
|
||||||
var actions = type.types.map(function (ele) { return getStringLiteral(ele); });
|
var actions = type.types.map(function (ele) { return checkStringLiteralLegal(filename, 'action', name.text, ele); });
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(type), "".concat(filename, "\u4E2D\u5F15\u7528\u7684action\u300C").concat(name.text, "\u300D\u7684\u5B9A\u4E49\u4E0D\u662Funion\u548CstringLiteral\u7C7B\u578B"));
|
(0, assert_1.default)(ts.isLiteralTypeNode(type), "".concat(filename, "\u4E2D\u5F15\u7528\u7684action\u300C").concat(name.text, "\u300D\u7684\u5B9A\u4E49\u4E0D\u662Funion\u548CstringLiteral\u7C7B\u578B"));
|
||||||
var action = getStringLiteral(type);
|
var action = checkStringLiteralLegal(filename, 'action', name.text, type);
|
||||||
return [action];
|
return [action];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -325,6 +325,25 @@ function checkLocaleExpressionPropertyExists(root, attr, exists, filename) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function getStringEnumValues(filename, program, obj, node) {
|
||||||
|
var _a;
|
||||||
|
var checker = program.getTypeChecker();
|
||||||
|
var symbol = checker.getSymbolAtLocation(node.typeName);
|
||||||
|
var declaration = symbol === null || symbol === void 0 ? void 0 : symbol.getDeclarations()[0];
|
||||||
|
if (ts.isImportSpecifier(declaration)) {
|
||||||
|
var typee = checker.getDeclaredTypeOfSymbol(symbol);
|
||||||
|
declaration = (_a = typee.aliasSymbol) === null || _a === void 0 ? void 0 : _a.getDeclarations()[0];
|
||||||
|
}
|
||||||
|
if (declaration && ts.isTypeAliasDeclaration(declaration)) {
|
||||||
|
if (ts.isUnionTypeNode(declaration.type) && ts.isLiteralTypeNode(declaration.type.types[0])) {
|
||||||
|
return declaration.type.types.map(function (ele) { return checkStringLiteralLegal(filename, obj, declaration.name.text, ele); });
|
||||||
|
}
|
||||||
|
if (ts.isLiteralTypeNode(declaration.type)) {
|
||||||
|
var value = checkStringLiteralLegal(filename, obj, declaration.name.text, declaration.type);
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
function analyzeEntity(filename, path, program, relativePath) {
|
function analyzeEntity(filename, path, program, relativePath) {
|
||||||
var _a;
|
var _a;
|
||||||
var fullPath = "".concat(path, "/").concat(filename);
|
var fullPath = "".concat(path, "/").concat(filename);
|
||||||
|
|
@ -347,9 +366,7 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
var toModi = false;
|
var toModi = false;
|
||||||
var actionType = 'crud';
|
var actionType = 'crud';
|
||||||
var _static = false;
|
var _static = false;
|
||||||
var enumStringAttrs = [];
|
var enumAttributes = {};
|
||||||
var states = [];
|
|
||||||
var localEnumStringTypes = [];
|
|
||||||
var additionalImports = [];
|
var additionalImports = [];
|
||||||
var localeDef = undefined;
|
var localeDef = undefined;
|
||||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||||
|
|
@ -419,8 +436,9 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
schemaAttrs.push(attrNode);
|
schemaAttrs.push(attrNode);
|
||||||
if (localEnumStringTypes.includes(type.typeName.text)) {
|
var enumStringValues = getStringEnumValues(filename, program, '属性', type);
|
||||||
enumStringAttrs.push(name.text);
|
if (enumStringValues) {
|
||||||
|
enumAttributes[attrName] = enumStringValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -447,15 +465,19 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
schemaAttrs.push(attrNode);
|
schemaAttrs.push(attrNode);
|
||||||
if (ts.isUnionTypeNode(type)) {
|
if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) && ts.isStringLiteral(type.types[0].literal)) {
|
||||||
|
(0, assert_1.default)(ts.isIdentifier(name));
|
||||||
var types = type.types;
|
var types = type.types;
|
||||||
if (ts.isLiteralTypeNode(types[0]) && ts.isStringLiteral(types[0].literal)) {
|
var enumValues = types.map(function (ele) { return checkStringLiteralLegal(filename, '属性', name.text, ele); });
|
||||||
enumStringAttrs.push(name.text);
|
enumAttributes[name.text] = enumValues;
|
||||||
types.forEach(function (ele) {
|
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "\u300C".concat(filename, "\u300D\u4E0D\u652F\u6301\u6DF7\u5408\u578B\u7684\u679A\u4E3E\u5C5E\u6027\u5B9A\u4E49\u300C").concat(attrName, "\u300D"));
|
|
||||||
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "\u300C".concat(filename, "\u300D\u4E2D\u5B9A\u4E49\u7684\u5C5E\u6027\u679A\u4E3E\u300C").concat(attrName, "\u300D\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u5E94\u5C0F\u4E8E").concat(env_1.STRING_LITERAL_MAX_LENGTH));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
else if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) {
|
||||||
|
// 单个字符串的情形,目前应该没有,没测试过,先写着 by Xc 20230221
|
||||||
|
(0, assert_1.default)(ts.isIdentifier(name));
|
||||||
|
var enumValues = [
|
||||||
|
checkStringLiteralLegal(filename, '属性', name.text, type)
|
||||||
|
];
|
||||||
|
enumAttributes[name.text] = enumValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attrName === 'entity') {
|
if (attrName === 'entity') {
|
||||||
|
|
@ -547,16 +569,19 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
(0, assert_1.default)(!hasActionDef, "\u3010".concat(filename, "\u3011action\u5B9A\u4E49\u987B\u5728Relation\u4E4B\u540E"));
|
(0, assert_1.default)(!hasActionDef, "\u3010".concat(filename, "\u3011action\u5B9A\u4E49\u987B\u5728Relation\u4E4B\u540E"));
|
||||||
(0, assert_1.default)(!localeDef, "\u3010".concat(filename, "\u3011locale\u5B9A\u4E49\u987B\u5728Relation\u4E4B\u540E"));
|
(0, assert_1.default)(!localeDef, "\u3010".concat(filename, "\u3011locale\u5B9A\u4E49\u987B\u5728Relation\u4E4B\u540E"));
|
||||||
// 增加userXXX对象的描述
|
// 增加userXXX对象的描述
|
||||||
|
var relationValues = [];
|
||||||
if (ts.isLiteralTypeNode(node.type)) {
|
if (ts.isLiteralTypeNode(node.type)) {
|
||||||
(0, assert_1.default)(ts.isStringLiteral(node.type.literal));
|
(0, assert_1.default)(ts.isStringLiteral(node.type.literal));
|
||||||
(0, assert_1.default)(node.type.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "Relation\u5B9A\u4E49\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u4E0D\u957F\u4E8E".concat(env_1.STRING_LITERAL_MAX_LENGTH, "\uFF08").concat(filename, "\uFF0C").concat(node.type.literal.text, "\uFF09"));
|
(0, assert_1.default)(node.type.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "Relation\u5B9A\u4E49\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u4E0D\u957F\u4E8E".concat(env_1.STRING_LITERAL_MAX_LENGTH, "\uFF08").concat(filename, "\uFF0C").concat(node.type.literal.text, "\uFF09"));
|
||||||
|
relationValues.push(node.type.literal.text);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(0, assert_1.default)(ts.isUnionTypeNode(node.type), "Relation\u7684\u5B9A\u4E49\u53EA\u80FD\u662Fstring\u7C7B\u578B\uFF08".concat(filename, "\uFF09"));
|
(0, assert_1.default)(ts.isUnionTypeNode(node.type), "Relation\u7684\u5B9A\u4E49\u53EA\u80FD\u662Fstring\u7C7B\u578B\uFF08".concat(filename, "\uFF09"));
|
||||||
node.type.types.forEach(function (ele) {
|
relationValues.push.apply(relationValues, tslib_1.__spreadArray([], tslib_1.__read(node.type.types.map(function (ele) {
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "Relation\u7684\u5B9A\u4E49\u53EA\u80FD\u662Fstring\u7C7B\u578B\uFF08".concat(filename, "\uFF09"));
|
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "Relation\u7684\u5B9A\u4E49\u53EA\u80FD\u662Fstring\u7C7B\u578B\uFF08".concat(filename, "\uFF09"));
|
||||||
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "Relation\u5B9A\u4E49\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u4E0D\u957F\u4E8E".concat(env_1.STRING_LITERAL_MAX_LENGTH, "\uFF08").concat(filename, "\uFF0C").concat(ele.literal.text, "\uFF09"));
|
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "Relation\u5B9A\u4E49\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u4E0D\u957F\u4E8E".concat(env_1.STRING_LITERAL_MAX_LENGTH, "\uFF08").concat(filename, "\uFF0C").concat(ele.literal.text, "\uFF09"));
|
||||||
});
|
return ele.literal.text;
|
||||||
|
})), false));
|
||||||
}
|
}
|
||||||
var entityLc = (0, string_1.firstLetterLowerCase)(moduleName);
|
var entityLc = (0, string_1.firstLetterLowerCase)(moduleName);
|
||||||
var relationEntityName = "User".concat(moduleName);
|
var relationEntityName = "User".concat(moduleName);
|
||||||
|
|
@ -569,7 +594,9 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
_d[relationEntityName] = {
|
_d[relationEntityName] = {
|
||||||
schemaAttrs: relationSchemaAttrs,
|
schemaAttrs: relationSchemaAttrs,
|
||||||
sourceFile: sourceFile,
|
sourceFile: sourceFile,
|
||||||
enumStringAttrs: ['relation'],
|
enumAttributes: {
|
||||||
|
relation: relationValues,
|
||||||
|
},
|
||||||
actionType: 'excludeUpdate',
|
actionType: 'excludeUpdate',
|
||||||
additionalImports: [
|
additionalImports: [
|
||||||
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation"))])), factory.createStringLiteral("../".concat(moduleName, "/Schema")), undefined)
|
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation"))])), factory.createStringLiteral("../".concat(moduleName, "/Schema")), undefined)
|
||||||
|
|
@ -611,58 +638,27 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
else if (beforeSchema) {
|
else if (beforeSchema) {
|
||||||
// 本地规定的一些形状定义,直接使用
|
// 本地规定的一些形状定义,直接使用
|
||||||
pushStatementIntoSchemaAst(moduleName, node, sourceFile);
|
pushStatementIntoSchemaAst(moduleName, node, sourceFile);
|
||||||
if (ts.isUnionTypeNode(node.type) && ts.isLiteralTypeNode(node.type.types[0]) && ts.isStringLiteral(node.type.types[0].literal)) {
|
|
||||||
// 本文件内定义的枚举类型
|
|
||||||
localEnumStringTypes.push(node.name.text);
|
|
||||||
node.type.types.forEach(function (ele) {
|
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), "\u300C".concat(filename, "\u300D\u4E0D\u652F\u6301\u6DF7\u5408\u578B\u7684\u5E38\u91CF\u5B9A\u4E49\u300C").concat(node.name.text, "\u300D"));
|
|
||||||
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "\u300C".concat(filename, "\u300D\u4E2D\u5B9A\u4E49\u7684\u5E38\u91CF\u679A\u4E3E\u300C").concat(node.name.text, "\u300D\u7684\u5B57\u7B26\u4E32\u957F\u5EA6\u5E94\u5C0F\u4E8E").concat(env_1.STRING_LITERAL_MAX_LENGTH));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ts.isVariableStatement(node)) {
|
if (ts.isVariableStatement(node)) {
|
||||||
var declarations = node.declarationList.declarations;
|
var declarations = node.declarationList.declarations;
|
||||||
declarations.forEach(function (declaration) {
|
declarations.forEach(function (declaration) {
|
||||||
if (ts.isIdentifier(declaration.name) && declaration.name.text.endsWith('ActionDef')) {
|
|
||||||
if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') {
|
if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') {
|
||||||
// 是显示的actionDef定义
|
|
||||||
checkActionDefNameConsistent(filename, declaration);
|
checkActionDefNameConsistent(filename, declaration);
|
||||||
var typeArguments = declaration.type.typeArguments;
|
var typeArguments = declaration.type.typeArguments;
|
||||||
(0, assert_1.default)(typeArguments.length === 2);
|
(0, assert_1.default)(typeArguments.length === 2);
|
||||||
var _a = tslib_1.__read(typeArguments, 2), actionNode = _a[0], stateNode = _a[1];
|
var _a = tslib_1.__read(typeArguments, 2), actionNode = _a[0], stateNode = _a[1];
|
||||||
var checker = program.getTypeChecker();
|
(0, assert_1.default)(ts.isTypeReferenceNode(actionNode));
|
||||||
var symbol = checker.getSymbolAtLocation(actionNode.typeName);
|
(0, assert_1.default)(ts.isTypeReferenceNode(stateNode));
|
||||||
var declaration2_1 = symbol.getDeclarations()[0];
|
(0, assert_1.default)(getStringEnumValues(filename, program, 'action', actionNode), "\u6587\u4EF6".concat(filename, "\u4E2D\u7684action").concat(actionNode.typeName.text, "\u5B9A\u4E49\u4E0D\u662F\u5B57\u7B26\u4E32\u7C7B\u578B"));
|
||||||
if (declaration2_1.getSourceFile() === sourceFile) {
|
var enumStateValues = getStringEnumValues(filename, program, 'state', stateNode);
|
||||||
// pushStatementIntoActionAst(moduleName, <ts.TypeAliasDeclaration>declaration2, sourceFile);
|
(0, assert_1.default)(enumStateValues, "\u6587\u4EF6".concat(filename, "\u4E2D\u7684state").concat(stateNode.typeName.text, "\u5B9A\u4E49\u4E0D\u662F\u5B57\u7B26\u4E32\u7C7B\u578B"));
|
||||||
}
|
|
||||||
symbol = checker.getSymbolAtLocation(stateNode.typeName);
|
|
||||||
declaration2_1 = symbol.getDeclarations()[0];
|
|
||||||
if (declaration2_1.getSourceFile() === sourceFile) {
|
|
||||||
// 检查state的定义合法
|
|
||||||
(0, assert_1.default)(ts.isTypeAliasDeclaration(declaration2_1) && ts.isUnionTypeNode(declaration2_1.type), "\u300C".concat(filename, "\u300DState\u300C").concat(declaration2_1.name, "\u300D\u7684\u5B9A\u4E49\u53EA\u80FD\u662F\u6216\u7ED3\u70B9"));
|
|
||||||
declaration2_1.type.types.forEach(function (type) {
|
|
||||||
(0, assert_1.default)(ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal), "\u300C".concat(filename, "\u300DState\u300C").concat(declaration2_1.name, "\u300D\u7684\u5B9A\u4E49\u53EA\u80FD\u662F\u5B57\u7B26\u4E32"));
|
|
||||||
(0, assert_1.default)(type.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, "\u300C".concat(filename, "\u300DState\u300C").concat(type.literal.text, "\u300D\u7684\u957F\u5EA6\u5927\u4E8E\u300C").concat(env_1.STRING_LITERAL_MAX_LENGTH, "\u300D"));
|
|
||||||
});
|
|
||||||
/* pushStatementIntoActionAst(moduleName,
|
|
||||||
factory.updateTypeAliasDeclaration(
|
|
||||||
declaration2,
|
|
||||||
declaration2.decorators,
|
|
||||||
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
||||||
declaration2.name,
|
|
||||||
declaration2.typeParameters,
|
|
||||||
declaration2.type
|
|
||||||
),
|
|
||||||
sourceFile); */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pushStatementIntoActionAst(moduleName, node, sourceFile);
|
pushStatementIntoActionAst(moduleName, node, sourceFile);
|
||||||
|
(0, assert_1.default)(ts.isIdentifier(declaration.name));
|
||||||
var adName = declaration.name.text.slice(0, declaration.name.text.length - 9);
|
var adName = declaration.name.text.slice(0, declaration.name.text.length - 9);
|
||||||
var attr = adName.concat('State');
|
var attr = adName.concat('State');
|
||||||
schemaAttrs.push(factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(attr)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(factory.createIdentifier(attr))));
|
schemaAttrs.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(attr), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(attr)));
|
||||||
states.push((0, string_1.firstLetterLowerCase)(attr));
|
enumAttributes[(0, string_1.firstLetterLowerCase)(attr)] = enumStateValues;
|
||||||
}
|
}
|
||||||
else if (declaration.type && (ts.isArrayTypeNode(declaration.type)
|
else if (declaration.type && (ts.isArrayTypeNode(declaration.type)
|
||||||
&& ts.isTypeReferenceNode(declaration.type.elementType)
|
&& ts.isTypeReferenceNode(declaration.type.elementType)
|
||||||
|
|
@ -783,13 +779,13 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
});
|
});
|
||||||
indexes = declaration.initializer;
|
indexes = declaration.initializer;
|
||||||
}
|
}
|
||||||
else if (ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') {
|
else if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') {
|
||||||
// locale定义
|
// locale定义
|
||||||
var type = declaration.type, initializer = declaration.initializer;
|
var type = declaration.type, initializer = declaration.initializer;
|
||||||
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer));
|
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer));
|
||||||
var properties = initializer.properties;
|
var properties = initializer.properties;
|
||||||
(0, assert_1.default)(properties.length > 0, "".concat(filename, "\u81F3\u5C11\u9700\u8981\u6709\u4E00\u79CDlocale\u5B9A\u4E49"));
|
(0, assert_1.default)(properties.length > 0, "".concat(filename, "\u81F3\u5C11\u9700\u8981\u6709\u4E00\u79CDlocale\u5B9A\u4E49"));
|
||||||
var allEnumStringAttrs = enumStringAttrs.concat(states);
|
var allEnumStringAttrs = Object.keys(enumAttributes);
|
||||||
var typeArguments = type.typeArguments;
|
var typeArguments = type.typeArguments;
|
||||||
(0, assert_1.default)(typeArguments &&
|
(0, assert_1.default)(typeArguments &&
|
||||||
ts.isTypeReferenceNode(typeArguments[0])
|
ts.isTypeReferenceNode(typeArguments[0])
|
||||||
|
|
@ -831,7 +827,7 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
}
|
}
|
||||||
localeDef = initializer;
|
localeDef = initializer;
|
||||||
}
|
}
|
||||||
else if (ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') {
|
else if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') {
|
||||||
(0, assert_1.default)(!hasActionDef, "".concat(moduleName, "\u4E2D\u7684Configuration\u5B9A\u4E49\u5728Action\u4E4B\u540E"));
|
(0, assert_1.default)(!hasActionDef, "".concat(moduleName, "\u4E2D\u7684Configuration\u5B9A\u4E49\u5728Action\u4E4B\u540E"));
|
||||||
(0, assert_1.default)(ts.isObjectLiteralExpression(declaration.initializer));
|
(0, assert_1.default)(ts.isObjectLiteralExpression(declaration.initializer));
|
||||||
var properties = declaration.initializer.properties;
|
var properties = declaration.initializer.properties;
|
||||||
|
|
@ -878,7 +874,7 @@ function analyzeEntity(filename, path, program, relativePath) {
|
||||||
actionType: actionType,
|
actionType: actionType,
|
||||||
static: _static,
|
static: _static,
|
||||||
hasRelationDef: hasRelationDef,
|
hasRelationDef: hasRelationDef,
|
||||||
enumStringAttrs: enumStringAttrs.concat(states),
|
enumAttributes: enumAttributes,
|
||||||
additionalImports: additionalImports,
|
additionalImports: additionalImports,
|
||||||
};
|
};
|
||||||
if (hasFulltextIndex) {
|
if (hasFulltextIndex) {
|
||||||
|
|
@ -2898,7 +2894,7 @@ function outputAction(outputDir, printer) {
|
||||||
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
|
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
|
||||||
}
|
}
|
||||||
function constructAttributes(entity) {
|
function constructAttributes(entity) {
|
||||||
var _a = Schema[entity], schemaAttrs = _a.schemaAttrs, enumStringAttrs = _a.enumStringAttrs;
|
var _a = Schema[entity], schemaAttrs = _a.schemaAttrs, enumAttributes = _a.enumAttributes;
|
||||||
var _b = ManyToOne, _c = entity, manyToOneSet = _b[_c];
|
var _b = ManyToOne, _c = entity, manyToOneSet = _b[_c];
|
||||||
var result = [];
|
var result = [];
|
||||||
schemaAttrs.forEach(function (attr) {
|
schemaAttrs.forEach(function (attr) {
|
||||||
|
|
@ -2983,8 +2979,8 @@ function constructAttributes(entity) {
|
||||||
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("ref")), factory.createPropertyAssignment(factory.createIdentifier("ref"), factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2_6))));
|
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("ref")), factory.createPropertyAssignment(factory.createIdentifier("ref"), factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2_6))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (enumStringAttrs && enumStringAttrs.includes(name.text)) {
|
if (enumAttributes && enumAttributes[name.text]) {
|
||||||
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(env_1.STRING_LITERAL_MAX_LENGTH))], true)));
|
attrAssignments.push(factory.createPropertyAssignment('type', factory.createStringLiteral("enum")), factory.createPropertyAssignment('enumeration', factory.createArrayLiteralExpression(enumAttributes[name.text].map(function (ele) { return factory.createStringLiteral(ele); }))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// todo 引用的非string定义,目前没有处理int类型的引用,等遇到了再处理
|
// todo 引用的非string定义,目前没有处理int类型的引用,等遇到了再处理
|
||||||
|
|
@ -3002,7 +2998,8 @@ function constructAttributes(entity) {
|
||||||
if (ts.isUnionTypeNode(type)) {
|
if (ts.isUnionTypeNode(type)) {
|
||||||
if (ts.isLiteralTypeNode(type.types[0])) {
|
if (ts.isLiteralTypeNode(type.types[0])) {
|
||||||
if (ts.isStringLiteral(type.types[0].literal)) {
|
if (ts.isStringLiteral(type.types[0].literal)) {
|
||||||
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(env_1.STRING_LITERAL_MAX_LENGTH))], true)));
|
(0, assert_1.default)(enumAttributes && enumAttributes[name.text]);
|
||||||
|
attrAssignments.push(factory.createPropertyAssignment('type', factory.createStringLiteral("enum")), factory.createPropertyAssignment('enumeration', factory.createArrayLiteralExpression(enumAttributes[name.text].map(function (ele) { return factory.createStringLiteral(ele); }))));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(0, assert_1.default)(ts.isNumericLiteral(type.types[0].literal));
|
(0, assert_1.default)(ts.isNumericLiteral(type.types[0].literal));
|
||||||
|
|
|
||||||
|
|
@ -583,6 +583,8 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
else {
|
else {
|
||||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
|
// 暂时先封掉user上的相关更新条件,会制造出连接表上的update
|
||||||
|
if (entity !== 'user') {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id && Object.keys(filter).length === 1) {
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
Object.assign(otm, {
|
Object.assign(otm, {
|
||||||
|
|
@ -608,6 +610,7 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// 基于foreignKey的one-to-many
|
// 基于foreignKey的one-to-many
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
|
|
@ -649,6 +652,8 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||||
|
// 暂时先封掉user上的连接,以避免生成连接表更新
|
||||||
|
if (entity !== 'user') {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id && Object.keys(filter).length === 1) {
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
Object.assign(otm, {
|
Object.assign(otm, {
|
||||||
|
|
@ -665,6 +670,7 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (action === 'remove' && actionOtm === 'update') {
|
if (action === 'remove' && actionOtm === 'update') {
|
||||||
Object.assign(dataOtm, (_f = {},
|
Object.assign(dataOtm, (_f = {},
|
||||||
_f[foreignKey_2] = null,
|
_f[foreignKey_2] = null,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { AuthDefDict, Checker, EntityDict, OperateOption, SelectOption, StorageS
|
||||||
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 { SyncContext } from './SyncRowStore';
|
import { SyncContext } from './SyncRowStore';
|
||||||
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): {
|
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
||||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
fn: Trigger<ED, T, Cxt>['fn'];
|
||||||
when: 'before' | 'after';
|
when: 'before' | 'after';
|
||||||
};
|
};
|
||||||
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ var relation_1 = require("./relation");
|
||||||
var uuid_1 = require("../utils/uuid");
|
var uuid_1 = require("../utils/uuid");
|
||||||
function translateCheckerInAsyncContext(checker) {
|
function translateCheckerInAsyncContext(checker) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
var entity = checker.entity, type = checker.type;
|
||||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
var when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'data': {
|
case 'data': {
|
||||||
var checkerFn_1 = checker.checker;
|
var checkerFn_1 = checker.checker;
|
||||||
|
|
@ -102,44 +102,36 @@ function translateCheckerInAsyncContext(checker) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'relation': {
|
case 'relation': {
|
||||||
var relationFilter_1 = checker.relationFilter, errMsg_2 = checker.errMsg;
|
var relationFilter_1 = checker.relationFilter, errMsg = checker.errMsg;
|
||||||
var fn = (function (_a, context, option) {
|
var fn = (function (_a, context, option) {
|
||||||
var operation = _a.operation;
|
var operation = _a.operation;
|
||||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||||
var filter2, data, filter, _b, _c, _d;
|
var result, _b;
|
||||||
return tslib_1.__generator(this, function (_e) {
|
return tslib_1.__generator(this, function (_c) {
|
||||||
switch (_e.label) {
|
switch (_c.label) {
|
||||||
case 0:
|
case 0:
|
||||||
if (context.isRoot()) {
|
if (context.isRoot()) {
|
||||||
return [2 /*return*/, 0];
|
return [2 /*return*/, 0];
|
||||||
}
|
}
|
||||||
if (!(operation.action === 'create')) return [3 /*break*/, 3];
|
if (!(typeof relationFilter_1 === 'function')) return [3 /*break*/, 2];
|
||||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
||||||
case 1:
|
case 1:
|
||||||
filter2 = _e.sent();
|
_b = _c.sent();
|
||||||
data = operation.data;
|
return [3 /*break*/, 3];
|
||||||
filter = data instanceof Array ? {
|
|
||||||
id: {
|
|
||||||
$in: data.map(function (ele) { return ele.id; }),
|
|
||||||
},
|
|
||||||
} : {
|
|
||||||
id: data.id,
|
|
||||||
};
|
|
||||||
return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, filter, true)];
|
|
||||||
case 2:
|
case 2:
|
||||||
if (_e.sent()) {
|
_b = relationFilter_1;
|
||||||
return [2 /*return*/, 0];
|
_c.label = 3;
|
||||||
}
|
|
||||||
throw new Exception_1.OakUserUnpermittedException(errMsg_2);
|
|
||||||
case 3:
|
case 3:
|
||||||
_b = operation;
|
result = _b;
|
||||||
_c = filter_1.combineFilters;
|
if (result) {
|
||||||
_d = [operation.filter];
|
if (operation.action === 'create') {
|
||||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
|
||||||
case 4:
|
}
|
||||||
_b.filter = _c.apply(void 0, [_d.concat([_e.sent()])]);
|
else {
|
||||||
_e.label = 5;
|
operation.filter = (0, filter_1.combineFilters)([operation.filter, result]);
|
||||||
case 5: return [2 /*return*/, 0];
|
}
|
||||||
|
}
|
||||||
|
return [2 /*return*/, 0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -181,8 +173,8 @@ function translateCheckerInAsyncContext(checker) {
|
||||||
}
|
}
|
||||||
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
||||||
function translateCheckerInSyncContext(checker) {
|
function translateCheckerInSyncContext(checker) {
|
||||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
var entity = checker.entity, type = checker.type;
|
||||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
var when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'data': {
|
case 'data': {
|
||||||
var checkerFn_3 = checker.checker;
|
var checkerFn_3 = checker.checker;
|
||||||
|
|
@ -193,7 +185,7 @@ function translateCheckerInSyncContext(checker) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'row': {
|
case 'row': {
|
||||||
var filter_3 = checker.filter, errMsg_3 = checker.errMsg;
|
var filter_3 = checker.filter, errMsg_2 = checker.errMsg;
|
||||||
var fn = function (operation, context, option) {
|
var fn = function (operation, context, option) {
|
||||||
var operationFilter = operation.filter, action = operation.action;
|
var operationFilter = operation.filter, action = operation.action;
|
||||||
var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3;
|
var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3;
|
||||||
|
|
@ -207,7 +199,7 @@ function translateCheckerInSyncContext(checker) {
|
||||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var e = new Exception_1.OakRowInconsistencyException(undefined, errMsg_3);
|
var e = new Exception_1.OakRowInconsistencyException(undefined, errMsg_2);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -217,28 +209,25 @@ function translateCheckerInSyncContext(checker) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'relation': {
|
case 'relation': {
|
||||||
var relationFilter_2 = checker.relationFilter, errMsg_4 = checker.errMsg;
|
var relationFilter_2 = checker.relationFilter, errMsg_3 = checker.errMsg;
|
||||||
var fn = function (operation, context, option) {
|
var fn = function (operation, context, option) {
|
||||||
if (context.isRoot()) {
|
if (context.isRoot()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var filter2 = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
|
var result = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
|
||||||
|
(0, assert_1.default)(!(result instanceof Promise));
|
||||||
|
if (result) {
|
||||||
var filter = operation.filter, action = operation.action;
|
var filter = operation.filter, action = operation.action;
|
||||||
var filter3 = filter;
|
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
var data = operation.data;
|
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
|
||||||
filter3 = data instanceof Array ? {
|
|
||||||
id: {
|
|
||||||
$in: data.map(function (ele) { return ele.id; }),
|
|
||||||
},
|
|
||||||
} : { id: data.id };
|
|
||||||
}
|
|
||||||
(0, assert_1.default)(filter3);
|
|
||||||
(0, assert_1.default)(!(filter2 instanceof Promise));
|
|
||||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, filter3, true)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Exception_1.OakUserUnpermittedException(errMsg_4);
|
(0, assert_1.default)(filter);
|
||||||
|
if ((0, filter_1.checkFilterContains)(entity, context, result, filter, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Exception_1.OakUserUnpermittedException(errMsg_3);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
fn: fn,
|
fn: fn,
|
||||||
|
|
@ -265,9 +254,12 @@ function translateCheckerInSyncContext(checker) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.translateCheckerInSyncContext = translateCheckerInSyncContext;
|
exports.translateCheckerInSyncContext = translateCheckerInSyncContext;
|
||||||
function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) {
|
||||||
var cascadePath = lch.cascadePath, relations = lch.relations;
|
var cascadePath = lch.cascadePath, relations = lch.relations;
|
||||||
var paths = cascadePath.split('.');
|
var paths = cascadePath ? cascadePath.split('.') : [];
|
||||||
|
if (pathPrefix) {
|
||||||
|
paths.unshift(pathPrefix);
|
||||||
|
}
|
||||||
var translateRelationFilter = function (entity) {
|
var translateRelationFilter = function (entity) {
|
||||||
// 有两种情况,此entity和user有Relation定义,或是此entity已经指向user
|
// 有两种情况,此entity和user有Relation定义,或是此entity已经指向user
|
||||||
if (entity === 'user') {
|
if (entity === 'user') {
|
||||||
|
|
@ -312,11 +304,12 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
||||||
};
|
};
|
||||||
var translateFilterMakerIter = function (entity, iter) {
|
var translateFilterMakerIter = function (entity, iter) {
|
||||||
var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]);
|
var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]);
|
||||||
|
(0, assert_1.default)(relation === 2 || typeof relation === 'string');
|
||||||
if (iter === paths.length - 1) {
|
if (iter === paths.length - 1) {
|
||||||
if (relation === 2) {
|
if (relation === 2) {
|
||||||
var filterMaker_1 = translateRelationFilter(paths[iter]);
|
var filterMaker2_1 = translateRelationFilter(paths[iter]);
|
||||||
return function (userId) {
|
return function (userId) {
|
||||||
var filter = filterMaker_1(userId);
|
var filter = filterMaker2_1(userId);
|
||||||
(0, assert_1.default)(filter.id);
|
(0, assert_1.default)(filter.id);
|
||||||
return {
|
return {
|
||||||
entity: paths[iter],
|
entity: paths[iter],
|
||||||
|
|
@ -324,11 +317,10 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(0, assert_1.default)(typeof relation === 'string');
|
var filterMaker2_2 = translateRelationFilter(relation);
|
||||||
var filterMaker_2 = translateRelationFilter(relation);
|
|
||||||
return function (userId) {
|
return function (userId) {
|
||||||
var _a;
|
var _a;
|
||||||
var filter = filterMaker_2(userId);
|
var filter = filterMaker2_2(userId);
|
||||||
(0, assert_1.default)(filter.id);
|
(0, assert_1.default)(filter.id);
|
||||||
return _a = {},
|
return _a = {},
|
||||||
_a["".concat(paths[iter], "Id")] = filter.id,
|
_a["".concat(paths[iter], "Id")] = filter.id,
|
||||||
|
|
@ -336,43 +328,272 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var subFilterMaker_1 = translateFilterMakerIter(paths[iter], iter + 1);
|
var filterMaker_1 = relation === 2 ? translateFilterMakerIter(paths[iter], iter + 1) : translateFilterMakerIter(relation, iter + 1);
|
||||||
if (iter === 0) {
|
|
||||||
return function (userId) {
|
|
||||||
var _a;
|
|
||||||
var subFilter = subFilterMaker_1(userId);
|
|
||||||
return _a = {},
|
|
||||||
_a[paths[iter]] = subFilter,
|
|
||||||
_a;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return function (userId) {
|
return function (userId) {
|
||||||
var _a;
|
var _a;
|
||||||
return (_a = {},
|
return (_a = {},
|
||||||
_a[paths[iter]] = subFilterMaker_1(userId),
|
_a[paths[iter]] = filterMaker_1(userId),
|
||||||
_a);
|
_a);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
var filterMaker = paths.length ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||||
return filter;
|
if (!paths.length) {
|
||||||
|
return function (oper, userId) { return filterMaker(userId); };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 针对第一层做一下特别优化,比如对象A指向对象B(多对一),如果A的cascadePath是 'B',
|
||||||
|
* 当create A时,会带有Bid。此时生成该B对象上的相关表达式查询返回,可以避免必须将此判定在对象创建之后再做
|
||||||
|
* 另一使用场景是,在查询A时,如果带有Bid(在对象跳一对多子对象场景下很常见),可以提前判定这个查询对某些用户一定返回空集
|
||||||
|
*/
|
||||||
|
var _a = tslib_1.__read(paths, 1), attr = _a[0];
|
||||||
|
var relation = (0, relation_1.judgeRelation)(schema, entity2, attr);
|
||||||
|
(0, assert_1.default)(relation === 2 || typeof relation === 'string');
|
||||||
|
var filterMaker2 = paths.length > 1
|
||||||
|
? (relation === 2 ? translateFilterMakerIter(attr, 1) : translateFilterMakerIter(relation, 1))
|
||||||
|
: (relation === 2 ? translateRelationFilter(attr) : translateRelationFilter(relation));
|
||||||
|
return function (operation, userId) {
|
||||||
|
var action = operation.action;
|
||||||
|
if (action === 'create') {
|
||||||
|
var data = operation.data;
|
||||||
|
var getForeignKeyId_1 = function (d) {
|
||||||
|
if (relation === 2) {
|
||||||
|
if (d.entity === attr && typeof d.entityId === 'string') {
|
||||||
|
return d.entitId;
|
||||||
|
}
|
||||||
|
throw new Exception_1.OakUserUnpermittedException();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.default)(typeof relation === 'string');
|
||||||
|
if (typeof d["".concat(attr, "Id")] === 'string') {
|
||||||
|
return d["".concat(attr, "Id")];
|
||||||
|
}
|
||||||
|
throw new Exception_1.OakUserUnpermittedException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (relation === 2) {
|
||||||
|
if (data instanceof Array) {
|
||||||
|
var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }));
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||||
|
$count: fkIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var fkId_1 = getForeignKeyId_1(data);
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId_1 }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(0, assert_1.default)(typeof relation === 'string');
|
||||||
|
if (data instanceof Array) {
|
||||||
|
var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }));
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||||
|
$count: fkIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var fkId = getForeignKeyId_1(data);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var filter = operation.filter;
|
||||||
|
if (relation === 2 && (filter === null || filter === void 0 ? void 0 : filter.entity) === attr && (filter === null || filter === void 0 ? void 0 : filter.entityId)) {
|
||||||
|
if (typeof filter.entityId === 'string') {
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: filter.entityId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (filter.entityId.$in && filter.entityId.$in instanceof Array) {
|
||||||
|
var entityIds = (0, lodash_1.uniq)(filter.entityId.$in);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||||
|
$count: entityIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filter && filter["".concat(attr, "Id")]) {
|
||||||
|
if (typeof filter["".concat(attr, "Id")] === 'string') {
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: filter["".concat(attr, "Id")] }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (filter["".concat(attr, "Id")].$in && filter["".concat(attr, "Id")].$in instanceof Array) {
|
||||||
|
var entityIds = (0, lodash_1.uniq)(filter["".concat(attr, "Id")].$in);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||||
|
$count: entityIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterMaker(userId);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
function translateActionAuthFilterMaker(schema, relationItem, entity, pathPrefix) {
|
||||||
if (relationItem instanceof Array) {
|
if (relationItem instanceof Array) {
|
||||||
var maker_1 = relationItem.map(function (ele) {
|
var maker = relationItem.map(function (ele) {
|
||||||
if (ele instanceof Array) {
|
if (ele instanceof Array) {
|
||||||
return ele.map(function (ele2) { return translateCascadeRelationFilterMaker(schema, ele2, entity); });
|
return ele.map(function (ele2) { return translateCascadeRelationFilterMaker(schema, ele2, entity, pathPrefix); });
|
||||||
}
|
}
|
||||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
return translateCascadeRelationFilterMaker(schema, ele, entity, pathPrefix);
|
||||||
});
|
});
|
||||||
return function (userId) { return ({
|
return maker;
|
||||||
$or: maker_1.map(function (ele) { return ({
|
|
||||||
$and: ele.map(function (ele2) { return ele2(userId); })
|
|
||||||
}); })
|
|
||||||
}); };
|
|
||||||
}
|
}
|
||||||
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity, pathPrefix);
|
||||||
return function (userId) { return filterMaker(userId); };
|
return filterMaker;
|
||||||
|
}
|
||||||
|
function makePotentialFilter(operation, context, filterMaker) {
|
||||||
|
var e_1, _a;
|
||||||
|
var userId = context.getCurrentUserId();
|
||||||
|
(0, assert_1.default)(userId);
|
||||||
|
var filters = filterMaker instanceof Array ? filterMaker.map(function (ele) {
|
||||||
|
if (ele instanceof Array) {
|
||||||
|
return ele.map(function (ele2) { return ele2(operation, userId); });
|
||||||
|
}
|
||||||
|
return ele(operation, userId);
|
||||||
|
}) : [filterMaker(operation, userId)];
|
||||||
|
/**
|
||||||
|
* 在下面的逻辑中,如果某个maker返回的是$entity类型,则检查是否有满足条件的项,没有就要抛出异常,有就返回undefined
|
||||||
|
* undefined项即意味着该条件通过
|
||||||
|
* 再加上and和or的布尔逻辑判断,得到最终结果
|
||||||
|
* 还要考虑同步和异步……
|
||||||
|
* 代码比较复杂,因为原先没有$entity这种返回结果的设计
|
||||||
|
* by Xc 20130219
|
||||||
|
*/
|
||||||
|
var filtersOr = [];
|
||||||
|
var isAsyncOr = false;
|
||||||
|
var _loop_1 = function (f) {
|
||||||
|
var e_2, _b;
|
||||||
|
if (f instanceof Array) {
|
||||||
|
var isAsyncAnd = true;
|
||||||
|
var filtersAnd = [];
|
||||||
|
var _loop_2 = function (ff) {
|
||||||
|
if (ff === null || ff === void 0 ? void 0 : ff.$entity) {
|
||||||
|
var _e = ff, $entity = _e.$entity, $filter = _e.$filter, _f = _e.$count, $count_1 = _f === void 0 ? 1 : _f;
|
||||||
|
var count = context.count($entity, {
|
||||||
|
filter: $filter,
|
||||||
|
}, {});
|
||||||
|
if (count instanceof Promise) {
|
||||||
|
isAsyncAnd = true;
|
||||||
|
filtersAnd.push(count.then(function (c2) {
|
||||||
|
if (c2 >= $count_1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return new Exception_1.OakUserUnpermittedException();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filtersAnd.push(count >= $count_1 ? undefined : new Exception_1.OakUserUnpermittedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ff) {
|
||||||
|
filtersAnd.push(ff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
for (var f_1 = (e_2 = void 0, tslib_1.__values(f)), f_1_1 = f_1.next(); !f_1_1.done; f_1_1 = f_1.next()) {
|
||||||
|
var ff = f_1_1.value;
|
||||||
|
_loop_2(ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (f_1_1 && !f_1_1.done && (_b = f_1.return)) _b.call(f_1);
|
||||||
|
}
|
||||||
|
finally { if (e_2) throw e_2.error; }
|
||||||
|
}
|
||||||
|
if (isAsyncAnd = true) {
|
||||||
|
isAsyncOr = true;
|
||||||
|
filtersOr.push(isAsyncAnd ? Promise.all(filtersAnd).then(function (fa) {
|
||||||
|
var e_3, _a;
|
||||||
|
var faR = [];
|
||||||
|
try {
|
||||||
|
for (var fa_1 = (e_3 = void 0, tslib_1.__values(fa)), fa_1_1 = fa_1.next(); !fa_1_1.done; fa_1_1 = fa_1.next()) {
|
||||||
|
var faItem = fa_1_1.value;
|
||||||
|
if (faItem instanceof Exception_1.OakUserUnpermittedException) {
|
||||||
|
return faItem;
|
||||||
|
}
|
||||||
|
else if (faItem) {
|
||||||
|
faR.push(faItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (fa_1_1 && !fa_1_1.done && (_a = fa_1.return)) _a.call(fa_1);
|
||||||
|
}
|
||||||
|
finally { if (e_3) throw e_3.error; }
|
||||||
|
}
|
||||||
|
if (faR.length > 0) {
|
||||||
|
return {
|
||||||
|
$and: faR,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}) : {
|
||||||
|
$and: filtersAnd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (f === null || f === void 0 ? void 0 : f.$entity) {
|
||||||
|
var _c = f, $entity = _c.$entity, $filter = _c.$filter, _d = _c.$count, $count_2 = _d === void 0 ? 1 : _d;
|
||||||
|
var count = context.count($entity, {
|
||||||
|
filter: $filter,
|
||||||
|
}, {});
|
||||||
|
if (count instanceof Promise) {
|
||||||
|
isAsyncOr = true;
|
||||||
|
filtersOr.push(count.then(function (c2) { return c2 >= $count_2 ? undefined : new Exception_1.OakUserUnpermittedException(); }));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filtersOr.push(count >= $count_2 ? undefined : new Exception_1.OakUserUnpermittedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (f) {
|
||||||
|
filtersOr.push(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
for (var filters_1 = tslib_1.__values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) {
|
||||||
|
var f = filters_1_1.value;
|
||||||
|
_loop_1(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
|
||||||
|
}
|
||||||
|
finally { if (e_1) throw e_1.error; }
|
||||||
|
}
|
||||||
|
// or的逻辑是,有一个成功就直接通过
|
||||||
|
var returnOrFilters = function (filters) {
|
||||||
|
if (filters.length === 0 || filters.includes(undefined)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
var foFilters = filters.filter(function (ele) { return ele !== undefined && !(ele instanceof Exception_1.OakUserUnpermittedException); });
|
||||||
|
if (foFilters.length > 0) {
|
||||||
|
return {
|
||||||
|
$or: foFilters,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Exception_1.OakUserUnpermittedException();
|
||||||
|
};
|
||||||
|
if (isAsyncOr) {
|
||||||
|
return Promise.all(filtersOr)
|
||||||
|
.then(function (filters) { return returnOrFilters(filters); });
|
||||||
|
}
|
||||||
|
return returnOrFilters(filtersOr);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 根据权限定义,创建出相应的checker
|
* 根据权限定义,创建出相应的checker
|
||||||
|
|
@ -382,75 +603,90 @@ function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
||||||
*/
|
*/
|
||||||
function createAuthCheckers(schema, authDict) {
|
function createAuthCheckers(schema, authDict) {
|
||||||
var checkers = [];
|
var checkers = [];
|
||||||
var _loop_1 = function (entity) {
|
var _loop_3 = function (entity) {
|
||||||
var _a;
|
var _a;
|
||||||
if (authDict[entity]) {
|
if (authDict[entity]) {
|
||||||
var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
|
var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
|
||||||
if (relationAuth) {
|
if (relationAuth) {
|
||||||
var raFilterMakerDict_1 = {};
|
var raFilterMakerDict_1 = {};
|
||||||
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
var userEntityName = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||||
for (var r in relationAuth) {
|
for (var r in relationAuth) {
|
||||||
Object.assign(raFilterMakerDict_1, (_a = {},
|
Object.assign(raFilterMakerDict_1, (_a = {},
|
||||||
_a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
|
_a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], userEntityName, entity),
|
||||||
_a));
|
_a));
|
||||||
}
|
}
|
||||||
var entityIdAttr_1 = "".concat(entity, "Id");
|
var entityIdAttr_1 = "".concat(entity, "Id");
|
||||||
checkers.push({
|
checkers.push({
|
||||||
entity: userEntityName_1,
|
entity: userEntityName,
|
||||||
action: 'create',
|
action: 'create',
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
relationFilter: function (operation, context) {
|
relationFilter: function (operation, context) {
|
||||||
var _a;
|
|
||||||
var data = operation.data;
|
var data = operation.data;
|
||||||
(0, assert_1.default)(!(data instanceof Array));
|
(0, assert_1.default)(!(data instanceof Array));
|
||||||
var _b = data, relation = _b.relation, _c = entityIdAttr_1, entityId = _b[_c];
|
var _a = data, relation = _a.relation, _b = entityIdAttr_1, entityId = _a[_b];
|
||||||
var userId = context.getCurrentUserId();
|
|
||||||
if (!raFilterMakerDict_1[relation]) {
|
if (!raFilterMakerDict_1[relation]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var filter = raFilterMakerDict_1[relation](userId);
|
var filter = makePotentialFilter(operation, context, raFilterMakerDict_1[relation]);
|
||||||
return _a = {},
|
return filter;
|
||||||
_a[entity] = filter,
|
|
||||||
_a;
|
|
||||||
},
|
},
|
||||||
errMsg: '越权操作',
|
errMsg: '越权操作',
|
||||||
});
|
});
|
||||||
checkers.push({
|
checkers.push({
|
||||||
entity: userEntityName_1,
|
entity: userEntityName,
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
relationFilter: function (operation, context) {
|
relationFilter: function (operation, context) {
|
||||||
var _a;
|
// 目前过不去
|
||||||
var userId = context.getCurrentUserId();
|
return undefined;
|
||||||
var filter = operation.filter;
|
/* const userId = context.getCurrentUserId();
|
||||||
var makeFilterFromRows = function (rows) {
|
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||||
var relations = (0, lodash_1.uniq)(rows.map(function (ele) { return ele.relation; }));
|
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]): SyncOrAsync<ED[keyof ED]['Selection']['filter']> => {
|
||||||
var entityIds = (0, lodash_1.uniq)(rows.map(function (ele) { return ele[entityIdAttr_1]; }));
|
const relations = uniq(rows.map(ele => ele.relation));
|
||||||
(0, assert_1.default)(entityIds.length === 1, "\u5728\u56DE\u6536".concat(userEntityName_1, "\u4E0A\u6743\u9650\u65F6\uFF0C\u5355\u6B21\u56DE\u6536\u6D89\u53CA\u5230\u4E86\u4E0D\u540C\u7684\u5BF9\u8C61\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u88AB\u5141\u8BB8"));
|
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
|
||||||
|
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
|
||||||
// const entityId = entityIds[0]!;
|
// const entityId = entityIds[0]!;
|
||||||
|
|
||||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||||
|
const filtersAnd = relations.map(
|
||||||
|
(relation) => raFilterMakerDict[relation!]
|
||||||
|
).filter(
|
||||||
|
ele => !!ele
|
||||||
|
).map(
|
||||||
|
ele => makePotentialFilter(operation, context, ele)
|
||||||
|
);
|
||||||
|
if (filtersAnd.find(ele => ele instanceof Promise)) {
|
||||||
|
return Promise.all(filtersAnd).then(
|
||||||
|
(fa) => {
|
||||||
|
if (fa.length > 0) {
|
||||||
return {
|
return {
|
||||||
$and: relations.map(function (relation) { return raFilterMakerDict_1[relation]; }).filter(function (ele) { return !!ele; }).map(function (ele) {
|
$and: fa,
|
||||||
var _a;
|
} as ED[keyof ED]['Selection']['filter'];
|
||||||
return (_a = {},
|
}
|
||||||
_a[entity] = ele(userId),
|
}
|
||||||
_a);
|
);
|
||||||
})
|
}
|
||||||
|
if (filtersAnd.length > 0) {
|
||||||
|
return {
|
||||||
|
$and: filtersAnd
|
||||||
|
} as ED[keyof ED]['Selection']['filter'];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
|
||||||
var toBeRemoved = context.select(userEntityName_1, {
|
const toBeRemoved = context.select(userEntityName, {
|
||||||
data: (_a = {
|
data: {
|
||||||
id: 1,
|
id: 1,
|
||||||
relation: 1
|
relation: 1,
|
||||||
|
[entityIdAttr]: 1,
|
||||||
},
|
},
|
||||||
_a[entityIdAttr_1] = 1,
|
filter,
|
||||||
_a),
|
|
||||||
filter: filter,
|
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
if (toBeRemoved instanceof Promise) {
|
if (toBeRemoved instanceof Promise) {
|
||||||
return toBeRemoved.then(function (rows) { return makeFilterFromRows(rows); });
|
return toBeRemoved.then(
|
||||||
|
(rows) => makeFilterFromRows(rows)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return makeFilterFromRows(toBeRemoved);
|
return makeFilterFromRows(toBeRemoved); */
|
||||||
},
|
},
|
||||||
errMsg: '越权操作',
|
errMsg: '越权操作',
|
||||||
});
|
});
|
||||||
|
|
@ -458,7 +694,7 @@ function createAuthCheckers(schema, authDict) {
|
||||||
// todo 等实现的时候再写
|
// todo 等实现的时候再写
|
||||||
}
|
}
|
||||||
if (actionAuth) {
|
if (actionAuth) {
|
||||||
var _loop_2 = function (a) {
|
var _loop_4 = function (a) {
|
||||||
var filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a], entity);
|
var filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a], entity);
|
||||||
checkers.push({
|
checkers.push({
|
||||||
entity: entity,
|
entity: entity,
|
||||||
|
|
@ -466,20 +702,20 @@ function createAuthCheckers(schema, authDict) {
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
relationFilter: function (operation, context) {
|
relationFilter: function (operation, context) {
|
||||||
// const { filter } = operation;
|
// const { filter } = operation;
|
||||||
var filter = filterMaker(context.getCurrentUserId());
|
var filter = makePotentialFilter(operation, context, filterMaker);
|
||||||
return filter;
|
return filter;
|
||||||
},
|
},
|
||||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
for (var a in actionAuth) {
|
for (var a in actionAuth) {
|
||||||
_loop_2(a);
|
_loop_4(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (var entity in schema) {
|
for (var entity in schema) {
|
||||||
_loop_1(entity);
|
_loop_3(entity);
|
||||||
}
|
}
|
||||||
return checkers;
|
return checkers;
|
||||||
}
|
}
|
||||||
|
|
@ -491,7 +727,7 @@ exports.createAuthCheckers = createAuthCheckers;
|
||||||
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||||
*/
|
*/
|
||||||
function createRemoveCheckers(schema, authDict) {
|
function createRemoveCheckers(schema, authDict) {
|
||||||
var e_1, _a;
|
var e_4, _a;
|
||||||
var checkers = [];
|
var checkers = [];
|
||||||
// 先建立所有的一对多的关系
|
// 先建立所有的一对多的关系
|
||||||
var OneToManyMatrix = {};
|
var OneToManyMatrix = {};
|
||||||
|
|
@ -506,7 +742,7 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var addToMtoEntity = function (e, fs) {
|
var addToMtoEntity = function (e, fs) {
|
||||||
var e_2, _a;
|
var e_5, _a;
|
||||||
var _b;
|
var _b;
|
||||||
try {
|
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()) {
|
for (var fs_1 = tslib_1.__values(fs), fs_1_1 = fs_1.next(); !fs_1_1.done; fs_1_1 = fs_1.next()) {
|
||||||
|
|
@ -519,12 +755,12 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (fs_1_1 && !fs_1_1.done && (_a = fs_1.return)) _a.call(fs_1);
|
if (fs_1_1 && !fs_1_1.done && (_a = fs_1.return)) _a.call(fs_1);
|
||||||
}
|
}
|
||||||
finally { if (e_2) throw e_2.error; }
|
finally { if (e_5) throw e_5.error; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (var entity in schema) {
|
for (var entity in schema) {
|
||||||
|
|
@ -548,16 +784,16 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
// 当删除一时,要确认多上面没有指向一的数据
|
// 当删除一时,要确认多上面没有指向一的数据
|
||||||
var entities = (0, lodash_1.union)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
var entities = (0, lodash_1.union)(Object.keys(OneToManyMatrix), Object.keys(OneToManyOnEntityMatrix));
|
||||||
var _loop_3 = function (entity) {
|
var _loop_5 = function (entity) {
|
||||||
checkers.push({
|
checkers.push({
|
||||||
entity: entity,
|
entity: entity,
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
type: 'logical',
|
type: 'logical',
|
||||||
checker: function (operation, context, option) {
|
checker: function (operation, context, option) {
|
||||||
var e_3, _a, e_4, _b;
|
var e_6, _a, e_7, _b;
|
||||||
var promises = [];
|
var promises = [];
|
||||||
if (OneToManyMatrix[entity]) {
|
if (OneToManyMatrix[entity]) {
|
||||||
var _loop_5 = function (otm) {
|
var _loop_7 = function (otm) {
|
||||||
var _g, _h;
|
var _g, _h;
|
||||||
var _j = tslib_1.__read(otm, 2), e = _j[0], attr = _j[1];
|
var _j = tslib_1.__read(otm, 2), e = _j[0], attr = _j[1];
|
||||||
var proj = (_g = {
|
var proj = (_g = {
|
||||||
|
|
@ -594,21 +830,21 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
for (var _c = (e_3 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
for (var _c = (e_6 = void 0, tslib_1.__values(OneToManyMatrix[entity])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||||
var otm = _d.value;
|
var otm = _d.value;
|
||||||
_loop_5(otm);
|
_loop_7(otm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
||||||
}
|
}
|
||||||
finally { if (e_3) throw e_3.error; }
|
finally { if (e_6) throw e_6.error; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (OneToManyOnEntityMatrix[entity]) {
|
if (OneToManyOnEntityMatrix[entity]) {
|
||||||
var _loop_6 = function (otm) {
|
var _loop_8 = function (otm) {
|
||||||
var _l, _m, _o;
|
var _l, _m, _o;
|
||||||
var proj = {
|
var proj = {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
@ -652,17 +888,17 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
for (var _e = (e_4 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
|
for (var _e = (e_7 = void 0, tslib_1.__values(OneToManyOnEntityMatrix[entity])), _f = _e.next(); !_f.done; _f = _e.next()) {
|
||||||
var otm = _f.value;
|
var otm = _f.value;
|
||||||
_loop_6(otm);
|
_loop_8(otm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
||||||
}
|
}
|
||||||
finally { if (e_4) throw e_4.error; }
|
finally { if (e_7) throw e_7.error; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (promises.length > 0) {
|
if (promises.length > 0) {
|
||||||
|
|
@ -674,23 +910,23 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
try {
|
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()) {
|
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;
|
var entity = entities_1_1.value;
|
||||||
_loop_3(entity);
|
_loop_5(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
||||||
}
|
}
|
||||||
finally { if (e_1) throw e_1.error; }
|
finally { if (e_4) throw e_4.error; }
|
||||||
}
|
}
|
||||||
var _loop_4 = function (entity) {
|
var _loop_6 = function (entity) {
|
||||||
var e_5, _b;
|
var e_8, _b;
|
||||||
var cascadeRemove = authDict[entity].cascadeRemove;
|
var cascadeRemove = authDict[entity].cascadeRemove;
|
||||||
if (cascadeRemove) {
|
if (cascadeRemove) {
|
||||||
var entitiesOnEntityAttr = [];
|
var entitiesOnEntityAttr = [];
|
||||||
var hasAllEntity = false;
|
var hasAllEntity = false;
|
||||||
var _loop_7 = function (attr) {
|
var _loop_9 = function (attr) {
|
||||||
if (attr === '@entity') {
|
if (attr === '@entity') {
|
||||||
hasAllEntity = true;
|
hasAllEntity = true;
|
||||||
return "continue";
|
return "continue";
|
||||||
|
|
@ -765,13 +1001,13 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (var attr in cascadeRemove) {
|
for (var attr in cascadeRemove) {
|
||||||
_loop_7(attr);
|
_loop_9(attr);
|
||||||
}
|
}
|
||||||
if (hasAllEntity) {
|
if (hasAllEntity) {
|
||||||
var attributes = schema[entity].attributes;
|
var attributes = schema[entity].attributes;
|
||||||
var ref = attributes.entity.ref;
|
var ref = attributes.entity.ref;
|
||||||
var restEntities = (0, lodash_1.difference)(ref, entitiesOnEntityAttr);
|
var restEntities = (0, lodash_1.difference)(ref, entitiesOnEntityAttr);
|
||||||
var _loop_8 = function (e) {
|
var _loop_10 = function (e) {
|
||||||
checkers.push({
|
checkers.push({
|
||||||
entity: e,
|
entity: e,
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
|
|
@ -805,24 +1041,24 @@ function createRemoveCheckers(schema, authDict) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
for (var restEntities_1 = (e_5 = void 0, tslib_1.__values(restEntities)), restEntities_1_1 = restEntities_1.next(); !restEntities_1_1.done; restEntities_1_1 = restEntities_1.next()) {
|
for (var restEntities_1 = (e_8 = void 0, tslib_1.__values(restEntities)), restEntities_1_1 = restEntities_1.next(); !restEntities_1_1.done; restEntities_1_1 = restEntities_1.next()) {
|
||||||
var e = restEntities_1_1.value;
|
var e = restEntities_1_1.value;
|
||||||
_loop_8(e);
|
_loop_10(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (restEntities_1_1 && !restEntities_1_1.done && (_b = restEntities_1.return)) _b.call(restEntities_1);
|
if (restEntities_1_1 && !restEntities_1_1.done && (_b = restEntities_1.return)) _b.call(restEntities_1);
|
||||||
}
|
}
|
||||||
finally { if (e_5) throw e_5.error; }
|
finally { if (e_8) throw e_8.error; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 注入声明的cascade删除时的外键处理动作
|
// 注入声明的cascade删除时的外键处理动作
|
||||||
for (var entity in authDict) {
|
for (var entity in authDict) {
|
||||||
_loop_4(entity);
|
_loop_6(entity);
|
||||||
}
|
}
|
||||||
return checkers;
|
return checkers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function addFilterSegment() {
|
||||||
}
|
}
|
||||||
var filter = {};
|
var filter = {};
|
||||||
filters.forEach(function (ele) {
|
filters.forEach(function (ele) {
|
||||||
var _a, _b, _c, _d;
|
var _a, _b, _c;
|
||||||
if (ele) {
|
if (ele) {
|
||||||
for (var k in ele) {
|
for (var k in ele) {
|
||||||
if (k === '$and') {
|
if (k === '$and') {
|
||||||
|
|
@ -24,25 +24,17 @@ function addFilterSegment() {
|
||||||
filter.$and = ele[k];
|
filter.$and = ele[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (k === '$or') {
|
|
||||||
if (filter.$or) {
|
|
||||||
(_b = filter.$or).push.apply(_b, tslib_1.__spreadArray([], tslib_1.__read(ele[k]), false));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filter.$or = ele[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (filter.hasOwnProperty(k)) {
|
else if (filter.hasOwnProperty(k)) {
|
||||||
if (filter.$and) {
|
if (filter.$and) {
|
||||||
filter.$and.push((_c = {},
|
filter.$and.push((_b = {},
|
||||||
_c[k] = ele[k],
|
_b[k] = ele[k],
|
||||||
_c));
|
_b));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
filter.$and = [
|
filter.$and = [
|
||||||
(_d = {},
|
(_c = {},
|
||||||
_d[k] = ele[k],
|
_c[k] = ele[k],
|
||||||
_d)
|
_c)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove } from ".";
|
import { CascadeActionAuth, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } from ".";
|
||||||
import { AsyncContext } from "../store/AsyncRowStore";
|
import { AsyncContext } from "../store/AsyncRowStore";
|
||||||
import { SyncContext } from "../store/SyncRowStore";
|
import { SyncContext } from "../store/SyncRowStore";
|
||||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||||
|
|
@ -12,21 +12,21 @@ 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) => any | Promise<any>;
|
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => SyncOrAsync<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) => SyncOrAsync<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>> = {
|
||||||
priority?: number;
|
priority?: number;
|
||||||
type: 'row';
|
type: 'row';
|
||||||
entity: T;
|
entity: T;
|
||||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||||
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>);
|
||||||
errMsg?: string;
|
errMsg?: string;
|
||||||
inconsistentRows?: {
|
inconsistentRows?: {
|
||||||
entity: keyof ED;
|
entity: keyof ED;
|
||||||
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
||||||
};
|
};
|
||||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>);
|
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||||
};
|
};
|
||||||
export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
|
@ -34,9 +34,9 @@ export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, C
|
||||||
entity: T;
|
entity: T;
|
||||||
when?: 'after';
|
when?: 'after';
|
||||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>;
|
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>;
|
||||||
errMsg: string;
|
errMsg: string;
|
||||||
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) => SyncOrAsync<ED[T]['Selection']['filter']>);
|
||||||
};
|
};
|
||||||
export declare type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
export declare type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
|
@ -44,8 +44,8 @@ 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) => any | Promise<any>;
|
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<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) => SyncOrAsync<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>> = {
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
|
@ -54,7 +54,7 @@ export declare type LogicalRelationChecker<ED extends EntityDict, T extends keyo
|
||||||
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) => any | Promise<any>;
|
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) => SyncOrAsync<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>;
|
||||||
export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,12 @@ export declare class OakCongruentRowExists<ED extends EntityDict, T extends keyo
|
||||||
export declare class OakDeadlock<ED extends EntityDict> extends OakUserException<ED> {
|
export declare class OakDeadlock<ED extends EntityDict> extends OakUserException<ED> {
|
||||||
constructor(message?: string | undefined);
|
constructor(message?: string | undefined);
|
||||||
}
|
}
|
||||||
|
export declare class OakPreConditionUnsetException<ED extends EntityDict> extends OakUserException<ED> {
|
||||||
|
entity?: keyof ED;
|
||||||
|
code?: string;
|
||||||
|
constructor(message?: string | undefined, entity?: keyof ED | undefined, code?: string | undefined);
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
export declare function makeException<ED extends EntityDict>(data: {
|
export declare function makeException<ED extends EntityDict>(data: {
|
||||||
name: string;
|
name: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.makeException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0;
|
exports.makeException = exports.OakPreConditionUnsetException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = 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 OakException = /** @class */ (function (_super) {
|
var OakException = /** @class */ (function (_super) {
|
||||||
|
|
@ -252,6 +252,25 @@ var OakDeadlock = /** @class */ (function (_super) {
|
||||||
}(OakUserException));
|
}(OakUserException));
|
||||||
exports.OakDeadlock = OakDeadlock;
|
exports.OakDeadlock = OakDeadlock;
|
||||||
;
|
;
|
||||||
|
var OakPreConditionUnsetException = /** @class */ (function (_super) {
|
||||||
|
tslib_1.__extends(OakPreConditionUnsetException, _super);
|
||||||
|
function OakPreConditionUnsetException(message, entity, code) {
|
||||||
|
var _this = _super.call(this, message || '前置条件不满足') || this;
|
||||||
|
_this.entity = entity,
|
||||||
|
_this.code = code;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
OakPreConditionUnsetException.prototype.toString = function () {
|
||||||
|
return JSON.stringify({
|
||||||
|
name: this.constructor.name,
|
||||||
|
message: this.message,
|
||||||
|
code: this.code,
|
||||||
|
entity: this.entity,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return OakPreConditionUnsetException;
|
||||||
|
}(OakUserException));
|
||||||
|
exports.OakPreConditionUnsetException = OakPreConditionUnsetException;
|
||||||
function makeException(data) {
|
function makeException(data) {
|
||||||
var name = data.name;
|
var name = data.name;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
|
@ -320,6 +339,11 @@ function makeException(data) {
|
||||||
e.setOpRecords(data.opRecords);
|
e.setOpRecords(data.opRecords);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
case 'OakPreConditionUnsetException': {
|
||||||
|
var e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
|
||||||
|
e.setOpRecords(data.opRecords);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,5 @@ declare type IsOptional<T, K extends keyof T> = {
|
||||||
export declare type OptionalKeys<T> = {
|
export declare type OptionalKeys<T> = {
|
||||||
[K in keyof T]: IsOptional<T, K>;
|
[K in keyof T]: IsOptional<T, K>;
|
||||||
}[keyof T];
|
}[keyof T];
|
||||||
|
export declare type SyncOrAsync<T> = T | Promise<T>;
|
||||||
export {};
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export interface Attribute {
|
||||||
notNull?: boolean;
|
notNull?: boolean;
|
||||||
unique?: boolean;
|
unique?: boolean;
|
||||||
sequenceStart?: number;
|
sequenceStart?: number;
|
||||||
|
enumeration?: string[];
|
||||||
}
|
}
|
||||||
export declare type Attributes<SH extends EntityShape> = Omit<{
|
export declare type Attributes<SH extends EntityShape> = Omit<{
|
||||||
[attrName in keyof SH]: Attribute;
|
[attrName in keyof SH]: Attribute;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { EntityDict } from "../types";
|
||||||
declare type ValidatorFunction = (text: string, size?: number) => string | boolean;
|
declare type ValidatorFunction = (text: string, size?: number) => string | boolean;
|
||||||
declare type ValidatorMoneyFunction = (text: string, zero?: boolean) => string | boolean;
|
declare type ValidatorMoneyFunction = (text: string, zero?: boolean) => string | boolean;
|
||||||
export declare const isMobile: ValidatorFunction;
|
export declare const isMobile: ValidatorFunction;
|
||||||
|
|
@ -18,6 +19,6 @@ export declare const isPhone: ValidatorFunction;
|
||||||
export declare const isNumber: ValidatorFunction;
|
export declare const isNumber: ValidatorFunction;
|
||||||
export declare const isMoney: ValidatorMoneyFunction;
|
export declare const isMoney: ValidatorMoneyFunction;
|
||||||
export declare const isVehicleNumber: ValidatorFunction;
|
export declare const isVehicleNumber: ValidatorFunction;
|
||||||
export declare function checkAttributesNotNull<T extends Record<string, any>>(entity: string, data: T, attributes: Array<keyof T>, allowEmpty?: true): void;
|
export declare function checkAttributesNotNull<ED extends EntityDict, T extends keyof EntityDict>(entity: T, data: Partial<ED[T]['CreateSingle']['data']>, attributes: Array<keyof ED[T]['CreateSingle']['data']>, allowEmpty?: true): void;
|
||||||
export declare function checkAttributesScope<T extends Record<string, any>>(entity: string, data: T, attributes: Array<keyof T>): void;
|
export declare function checkAttributesScope<ED extends EntityDict, T extends keyof EntityDict>(entity: T, data: Partial<ED[T]['CreateSingle']['data']>, attributes: Array<keyof ED[T]['CreateSingle']['data']>): void;
|
||||||
export {};
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-domain",
|
"name": "oak-domain",
|
||||||
"version": "2.6.2",
|
"version": "2.6.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "XuChang"
|
"name": "XuChang"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ const Schema: Record<string, {
|
||||||
schemaAttrs: Array<ts.PropertySignature>;
|
schemaAttrs: Array<ts.PropertySignature>;
|
||||||
fulltextIndex?: true;
|
fulltextIndex?: true;
|
||||||
indexes?: ts.ArrayLiteralExpression;
|
indexes?: ts.ArrayLiteralExpression;
|
||||||
states: string[];
|
|
||||||
sourceFile: ts.SourceFile;
|
sourceFile: ts.SourceFile;
|
||||||
|
enumAttributes: Record<string, string[]>;
|
||||||
locale: ts.ObjectLiteralExpression;
|
locale: ts.ObjectLiteralExpression;
|
||||||
// relationHierarchy?: ts.ObjectLiteralExpression;
|
// relationHierarchy?: ts.ObjectLiteralExpression;
|
||||||
// reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
|
// reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
|
||||||
|
|
@ -31,7 +31,6 @@ const Schema: Record<string, {
|
||||||
static: boolean;
|
static: boolean;
|
||||||
inModi: boolean;
|
inModi: boolean;
|
||||||
hasRelationDef: false | ts.TypeAliasDeclaration;
|
hasRelationDef: false | ts.TypeAliasDeclaration;
|
||||||
enumStringAttrs: string[],
|
|
||||||
additionalImports: ts.ImportDeclaration[],
|
additionalImports: ts.ImportDeclaration[],
|
||||||
}> = {};
|
}> = {};
|
||||||
const OneToMany: Record<string, Array<[string, string, boolean]>> = {};
|
const OneToMany: Record<string, Array<[string, string, boolean]>> = {};
|
||||||
|
|
@ -265,6 +264,15 @@ function checkActionDefNameConsistent(filename: string, actionDefNode: ts.Variab
|
||||||
assert(adfName === aName && aName === sName, `文件${filename}中的ActionDef${name.text}中ActionDef, Action和State的命名规则不一致`);
|
assert(adfName === aName && aName === sName, `文件${filename}中的ActionDef${name.text}中ActionDef, Action和State的命名规则不一致`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkStringLiteralLegal(filename: string, obj: string, text: string, ele: ts.TypeNode) {
|
||||||
|
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `${filename}中引用的${obj} ${text}中存在不是stringliteral的类型`);
|
||||||
|
assert(!ele.literal.text.includes('$'), `${filename}中引用的action${text}中的${obj}「${ele.literal.text}」包含非法字符$`);
|
||||||
|
assert(ele.literal.text.length > 0, `${filename}中引用的action${text}中的${obj}「${ele.literal.text}」长度非法`);
|
||||||
|
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `${filename}中引用的${obj} ${text}中的「${ele.literal.text}」长度过长`);
|
||||||
|
return ele.literal.text;
|
||||||
|
}
|
||||||
|
|
||||||
function addActionSource(moduleName: string, name: ts.Identifier, node: ts.ImportDeclaration) {
|
function addActionSource(moduleName: string, name: ts.Identifier, node: ts.ImportDeclaration) {
|
||||||
const ast = ActionAsts[moduleName];
|
const ast = ActionAsts[moduleName];
|
||||||
const { moduleSpecifier } = node;
|
const { moduleSpecifier } = node;
|
||||||
|
|
@ -308,24 +316,17 @@ function getStringTextFromUnionStringLiterals(moduleName: string, filename: stri
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStringLiteral = (ele: ts.TypeNode) => {
|
|
||||||
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `${filename}中引用的action${(<ts.Identifier>name).text}中存在不是stringliteral的类型`);
|
|
||||||
assert(!ele.literal.text.includes('$'), `${filename}中引用的action${(<ts.Identifier>name).text}中的action「${ele.literal.text}」包含非法字符$`);
|
|
||||||
assert(ele.literal.text.length > 0, `${filename}中引用的action${(<ts.Identifier>name).text}中的action「${ele.literal.text}」长度非法`);
|
|
||||||
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `${filename}中引用的action${(<ts.Identifier>name).text}中的action「${ele.literal.text}」长度过长`);
|
|
||||||
return ele.literal.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ts.isUnionTypeNode(type)) {
|
if (ts.isUnionTypeNode(type)) {
|
||||||
const actions = type.types!.map(
|
const actions = type.types!.map(
|
||||||
ele => getStringLiteral(ele)
|
ele => checkStringLiteralLegal(filename, 'action', (<ts.Identifier>name).text, ele)
|
||||||
);
|
);
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(ts.isLiteralTypeNode(type!), `${filename}中引用的action「${(<ts.Identifier>name).text}」的定义不是union和stringLiteral类型`);
|
assert(ts.isLiteralTypeNode(type!), `${filename}中引用的action「${(<ts.Identifier>name).text}」的定义不是union和stringLiteral类型`);
|
||||||
const action = getStringLiteral(type);
|
const action = checkStringLiteralLegal(filename, 'action', (<ts.Identifier>name).text, type);
|
||||||
return [action];
|
return [action];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -487,6 +488,28 @@ function checkLocaleExpressionPropertyExists(root: ts.ObjectLiteralExpression, a
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStringEnumValues(filename: string, program: ts.Program, obj: string, node: ts.TypeReferenceNode) {
|
||||||
|
const checker = program.getTypeChecker();
|
||||||
|
const symbol = checker.getSymbolAtLocation(node.typeName);
|
||||||
|
let declaration = symbol?.getDeclarations()![0]!;
|
||||||
|
if (ts.isImportSpecifier(declaration)) {
|
||||||
|
const typee = checker.getDeclaredTypeOfSymbol(symbol!);
|
||||||
|
declaration = typee.aliasSymbol?.getDeclarations()![0]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration && ts.isTypeAliasDeclaration(declaration)) {
|
||||||
|
if (ts.isUnionTypeNode(declaration.type) && ts.isLiteralTypeNode(declaration.type.types[0])) {
|
||||||
|
return declaration.type.types.map(
|
||||||
|
ele => checkStringLiteralLegal(filename, obj, (<ts.TypeAliasDeclaration>declaration).name.text, ele)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (ts.isLiteralTypeNode(declaration.type)) {
|
||||||
|
const value = checkStringLiteralLegal(filename, obj, declaration.name.text, declaration.type);
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function analyzeEntity(filename: string, path: string, program: ts.Program, relativePath?: string) {
|
function analyzeEntity(filename: string, path: string, program: ts.Program, relativePath?: string) {
|
||||||
const fullPath = `${path}/${filename}`;
|
const fullPath = `${path}/${filename}`;
|
||||||
const sourceFile = program.getSourceFile(fullPath);
|
const sourceFile = program.getSourceFile(fullPath);
|
||||||
|
|
@ -509,9 +532,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
let toModi = false;
|
let toModi = false;
|
||||||
let actionType = 'crud';
|
let actionType = 'crud';
|
||||||
let _static = false;
|
let _static = false;
|
||||||
const enumStringAttrs: string[] = [];
|
const enumAttributes: Record<string, string[]> = {};
|
||||||
const states: string[] = [];
|
|
||||||
const localEnumStringTypes: string[] = [];
|
|
||||||
const additionalImports: ts.ImportDeclaration[] = [];
|
const additionalImports: ts.ImportDeclaration[] = [];
|
||||||
let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
|
let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
|
||||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||||
|
|
@ -598,8 +619,9 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
schemaAttrs.push(attrNode);
|
schemaAttrs.push(attrNode);
|
||||||
if (localEnumStringTypes.includes(type.typeName.text)) {
|
const enumStringValues = getStringEnumValues(filename, program, '属性', type);
|
||||||
enumStringAttrs.push((<ts.Identifier>name).text);
|
if (enumStringValues) {
|
||||||
|
enumAttributes[attrName] = enumStringValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -628,17 +650,21 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
schemaAttrs.push(attrNode);
|
schemaAttrs.push(attrNode);
|
||||||
if (ts.isUnionTypeNode(type!)) {
|
if (ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) && ts.isStringLiteral(type.types[0].literal)) {
|
||||||
|
assert(ts.isIdentifier(name));
|
||||||
const { types } = type;
|
const { types } = type;
|
||||||
if (ts.isLiteralTypeNode(types[0]) && ts.isStringLiteral(types[0].literal)) {
|
const enumValues = types.map(
|
||||||
enumStringAttrs.push((<ts.Identifier>name).text);
|
(ele) => checkStringLiteralLegal(filename, '属性', name.text, ele)
|
||||||
types.forEach(
|
);
|
||||||
(ele) => {
|
enumAttributes[name.text] = enumValues;
|
||||||
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `「${filename}」不支持混合型的枚举属性定义「${attrName}」`);
|
|
||||||
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `「${filename}」中定义的属性枚举「${attrName}」的字符串长度应小于${STRING_LITERAL_MAX_LENGTH}`);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
else if (ts.isLiteralTypeNode(type!) && ts.isStringLiteral(type.literal)) {
|
||||||
|
// 单个字符串的情形,目前应该没有,没测试过,先写着 by Xc 20230221
|
||||||
|
assert(ts.isIdentifier(name));
|
||||||
|
const enumValues = [
|
||||||
|
checkStringLiteralLegal(filename, '属性', name.text, type)
|
||||||
|
];
|
||||||
|
enumAttributes[name.text] = enumValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -771,18 +797,21 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
assert(!hasActionDef, `【${filename}】action定义须在Relation之后`);
|
assert(!hasActionDef, `【${filename}】action定义须在Relation之后`);
|
||||||
assert(!localeDef, `【${filename}】locale定义须在Relation之后`);
|
assert(!localeDef, `【${filename}】locale定义须在Relation之后`);
|
||||||
// 增加userXXX对象的描述
|
// 增加userXXX对象的描述
|
||||||
|
const relationValues = [] as string[];
|
||||||
if (ts.isLiteralTypeNode(node.type)) {
|
if (ts.isLiteralTypeNode(node.type)) {
|
||||||
assert(ts.isStringLiteral(node.type.literal));
|
assert(ts.isStringLiteral(node.type.literal));
|
||||||
assert(node.type.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${node.type.literal.text})`);
|
assert(node.type.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${node.type.literal.text})`);
|
||||||
|
relationValues.push(node.type.literal.text);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert(ts.isUnionTypeNode(node.type), `Relation的定义只能是string类型(${filename})`);
|
assert(ts.isUnionTypeNode(node.type), `Relation的定义只能是string类型(${filename})`);
|
||||||
node.type.types.forEach(
|
relationValues.push(...node.type.types.map(
|
||||||
(ele) => {
|
(ele) => {
|
||||||
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `Relation的定义只能是string类型(${filename})`);
|
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `Relation的定义只能是string类型(${filename})`);
|
||||||
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${ele.literal.text})`);
|
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${ele.literal.text})`);
|
||||||
|
return ele.literal.text;
|
||||||
}
|
}
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
const entityLc = firstLetterLowerCase(moduleName);
|
const entityLc = firstLetterLowerCase(moduleName);
|
||||||
const relationEntityName = `User${moduleName}`;
|
const relationEntityName = `User${moduleName}`;
|
||||||
|
|
@ -819,7 +848,9 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
[relationEntityName]: {
|
[relationEntityName]: {
|
||||||
schemaAttrs: relationSchemaAttrs,
|
schemaAttrs: relationSchemaAttrs,
|
||||||
sourceFile,
|
sourceFile,
|
||||||
enumStringAttrs: ['relation'],
|
enumAttributes: {
|
||||||
|
relation: relationValues,
|
||||||
|
},
|
||||||
actionType: 'excludeUpdate',
|
actionType: 'excludeUpdate',
|
||||||
additionalImports: [
|
additionalImports: [
|
||||||
factory.createImportDeclaration(
|
factory.createImportDeclaration(
|
||||||
|
|
@ -896,18 +927,6 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
else if (beforeSchema) {
|
else if (beforeSchema) {
|
||||||
// 本地规定的一些形状定义,直接使用
|
// 本地规定的一些形状定义,直接使用
|
||||||
pushStatementIntoSchemaAst(moduleName, node, sourceFile!);
|
pushStatementIntoSchemaAst(moduleName, node, sourceFile!);
|
||||||
|
|
||||||
if (ts.isUnionTypeNode(node.type) && ts.isLiteralTypeNode(node.type.types[0]) && ts.isStringLiteral(node.type.types[0].literal)) {
|
|
||||||
// 本文件内定义的枚举类型
|
|
||||||
localEnumStringTypes.push(node.name.text);
|
|
||||||
|
|
||||||
node.type.types.forEach(
|
|
||||||
(ele) => {
|
|
||||||
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `「${filename}」不支持混合型的常量定义「${node.name.text}」`);
|
|
||||||
assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `「${filename}」中定义的常量枚举「${node.name.text}」的字符串长度应小于${STRING_LITERAL_MAX_LENGTH}`);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -915,62 +934,34 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
const { declarationList: { declarations } } = node;
|
const { declarationList: { declarations } } = node;
|
||||||
declarations.forEach(
|
declarations.forEach(
|
||||||
(declaration) => {
|
(declaration) => {
|
||||||
if (ts.isIdentifier(declaration.name) && declaration.name.text.endsWith('ActionDef')) {
|
|
||||||
if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') {
|
if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') {
|
||||||
// 是显示的actionDef定义
|
|
||||||
checkActionDefNameConsistent(filename, declaration);
|
checkActionDefNameConsistent(filename, declaration);
|
||||||
const { typeArguments } = declaration.type;
|
const { typeArguments } = declaration.type;
|
||||||
assert(typeArguments!.length === 2);
|
assert(typeArguments!.length === 2);
|
||||||
const [actionNode, stateNode] = typeArguments!;
|
const [actionNode, stateNode] = typeArguments!;
|
||||||
|
|
||||||
const checker = program.getTypeChecker();
|
assert(ts.isTypeReferenceNode(actionNode));
|
||||||
let symbol = checker.getSymbolAtLocation((<ts.TypeReferenceNode>actionNode).typeName);
|
assert(ts.isTypeReferenceNode(stateNode));
|
||||||
|
assert(getStringEnumValues(filename, program, 'action', (<ts.TypeReferenceNode>actionNode)), `文件${filename}中的action${(<ts.Identifier>actionNode.typeName).text}定义不是字符串类型`);
|
||||||
let declaration2 = symbol!.getDeclarations()![0];
|
const enumStateValues = getStringEnumValues(filename, program, 'state', stateNode);
|
||||||
if (declaration2.getSourceFile() === sourceFile) {
|
assert(enumStateValues, `文件${filename}中的state${(<ts.Identifier>stateNode.typeName).text}定义不是字符串类型`)
|
||||||
// pushStatementIntoActionAst(moduleName, <ts.TypeAliasDeclaration>declaration2, sourceFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol = checker.getSymbolAtLocation((<ts.TypeReferenceNode>stateNode).typeName);
|
|
||||||
|
|
||||||
declaration2 = symbol!.getDeclarations()![0];
|
|
||||||
if (declaration2.getSourceFile() === sourceFile) {
|
|
||||||
// 检查state的定义合法
|
|
||||||
assert(ts.isTypeAliasDeclaration(declaration2) && ts.isUnionTypeNode(declaration2.type), `「${filename}」State「${(<ts.TypeAliasDeclaration>declaration2).name}」的定义只能是或结点`);
|
|
||||||
declaration2.type.types.forEach(
|
|
||||||
(type) => {
|
|
||||||
assert(ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal), `「${filename}」State「${(<ts.TypeAliasDeclaration>declaration2).name}」的定义只能是字符串`);
|
|
||||||
assert(type.literal.text.length < STRING_LITERAL_MAX_LENGTH, `「${filename}」State「${type.literal.text}」的长度大于「${STRING_LITERAL_MAX_LENGTH}」`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/* pushStatementIntoActionAst(moduleName,
|
|
||||||
factory.updateTypeAliasDeclaration(
|
|
||||||
declaration2,
|
|
||||||
declaration2.decorators,
|
|
||||||
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
||||||
declaration2.name,
|
|
||||||
declaration2.typeParameters,
|
|
||||||
declaration2.type
|
|
||||||
),
|
|
||||||
sourceFile); */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pushStatementIntoActionAst(moduleName, node, sourceFile!);
|
pushStatementIntoActionAst(moduleName, node, sourceFile!);
|
||||||
|
|
||||||
|
assert(ts.isIdentifier(declaration.name));
|
||||||
const adName = declaration.name.text.slice(0, declaration.name.text.length - 9);
|
const adName = declaration.name.text.slice(0, declaration.name.text.length - 9);
|
||||||
const attr = adName.concat('State');
|
const attr = adName.concat('State');
|
||||||
schemaAttrs.push(
|
schemaAttrs.push(
|
||||||
factory.createPropertySignature(
|
factory.createPropertySignature(
|
||||||
undefined,
|
undefined,
|
||||||
factory.createIdentifier(firstLetterLowerCase(attr)),
|
firstLetterLowerCase(attr),
|
||||||
factory.createToken(ts.SyntaxKind.QuestionToken),
|
factory.createToken(ts.SyntaxKind.QuestionToken),
|
||||||
factory.createTypeReferenceNode(
|
factory.createTypeReferenceNode(
|
||||||
factory.createIdentifier(attr),
|
attr,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
states.push(firstLetterLowerCase(attr));
|
enumAttributes[firstLetterLowerCase(attr)] = enumStateValues;
|
||||||
}
|
}
|
||||||
else if (declaration.type && (ts.isArrayTypeNode(declaration.type!)
|
else if (declaration.type && (ts.isArrayTypeNode(declaration.type!)
|
||||||
&& ts.isTypeReferenceNode(declaration.type.elementType)
|
&& ts.isTypeReferenceNode(declaration.type.elementType)
|
||||||
|
|
@ -1115,7 +1106,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
|
|
||||||
indexes = declaration.initializer;
|
indexes = declaration.initializer;
|
||||||
}
|
}
|
||||||
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') {
|
else if (declaration.type && ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') {
|
||||||
// locale定义
|
// locale定义
|
||||||
const { type, initializer } = declaration;
|
const { type, initializer } = declaration;
|
||||||
|
|
||||||
|
|
@ -1124,7 +1115,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
assert(properties.length > 0, `${filename}至少需要有一种locale定义`);
|
assert(properties.length > 0, `${filename}至少需要有一种locale定义`);
|
||||||
|
|
||||||
|
|
||||||
const allEnumStringAttrs = enumStringAttrs.concat(states);
|
const allEnumStringAttrs = Object.keys(enumAttributes);
|
||||||
const { typeArguments } = type;
|
const { typeArguments } = type;
|
||||||
assert(typeArguments &&
|
assert(typeArguments &&
|
||||||
ts.isTypeReferenceNode(typeArguments[0])
|
ts.isTypeReferenceNode(typeArguments[0])
|
||||||
|
|
@ -1170,7 +1161,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
|
|
||||||
localeDef = initializer;
|
localeDef = initializer;
|
||||||
}
|
}
|
||||||
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') {
|
else if (declaration.type && ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') {
|
||||||
assert(!hasActionDef, `${moduleName}中的Configuration定义在Action之后`);
|
assert(!hasActionDef, `${moduleName}中的Configuration定义在Action之后`);
|
||||||
assert(ts.isObjectLiteralExpression(declaration.initializer!));
|
assert(ts.isObjectLiteralExpression(declaration.initializer!));
|
||||||
const { properties } = declaration.initializer;
|
const { properties } = declaration.initializer;
|
||||||
|
|
@ -1223,7 +1214,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
||||||
actionType,
|
actionType,
|
||||||
static: _static,
|
static: _static,
|
||||||
hasRelationDef,
|
hasRelationDef,
|
||||||
enumStringAttrs: enumStringAttrs.concat(states),
|
enumAttributes,
|
||||||
additionalImports,
|
additionalImports,
|
||||||
};
|
};
|
||||||
if (hasFulltextIndex) {
|
if (hasFulltextIndex) {
|
||||||
|
|
@ -5244,7 +5235,7 @@ function outputAction(outputDir: string, printer: ts.Printer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructAttributes(entity: string): ts.PropertyAssignment[] {
|
function constructAttributes(entity: string): ts.PropertyAssignment[] {
|
||||||
const { schemaAttrs, enumStringAttrs } = Schema[entity];
|
const { schemaAttrs, enumAttributes } = Schema[entity];
|
||||||
const { [entity]: manyToOneSet } = ManyToOne;
|
const { [entity]: manyToOneSet } = ManyToOne;
|
||||||
const result: ts.PropertyAssignment[] = [];
|
const result: ts.PropertyAssignment[] = [];
|
||||||
|
|
||||||
|
|
@ -5458,20 +5449,18 @@ function constructAttributes(entity: string): ts.PropertyAssignment[] {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (enumStringAttrs && enumStringAttrs.includes((<ts.Identifier>name).text)) {
|
if (enumAttributes && enumAttributes[(<ts.Identifier>name).text]) {
|
||||||
attrAssignments.push(
|
attrAssignments.push(
|
||||||
factory.createPropertyAssignment(
|
factory.createPropertyAssignment(
|
||||||
factory.createIdentifier("type"),
|
'type',
|
||||||
factory.createStringLiteral("varchar")
|
factory.createStringLiteral("enum")
|
||||||
),
|
),
|
||||||
factory.createPropertyAssignment(
|
factory.createPropertyAssignment(
|
||||||
factory.createIdentifier("params"),
|
'enumeration',
|
||||||
factory.createObjectLiteralExpression(
|
factory.createArrayLiteralExpression(
|
||||||
[factory.createPropertyAssignment(
|
enumAttributes[(<ts.Identifier>name).text].map(
|
||||||
factory.createIdentifier("length"),
|
ele => factory.createStringLiteral(ele)
|
||||||
factory.createNumericLiteral(STRING_LITERAL_MAX_LENGTH)
|
)
|
||||||
)],
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -5497,19 +5486,18 @@ function constructAttributes(entity: string): ts.PropertyAssignment[] {
|
||||||
if (ts.isUnionTypeNode(type!)) {
|
if (ts.isUnionTypeNode(type!)) {
|
||||||
if (ts.isLiteralTypeNode(type.types[0])) {
|
if (ts.isLiteralTypeNode(type.types[0])) {
|
||||||
if (ts.isStringLiteral(type.types[0].literal)) {
|
if (ts.isStringLiteral(type.types[0].literal)) {
|
||||||
|
assert (enumAttributes && enumAttributes[(<ts.Identifier>name).text]);
|
||||||
attrAssignments.push(
|
attrAssignments.push(
|
||||||
factory.createPropertyAssignment(
|
factory.createPropertyAssignment(
|
||||||
factory.createIdentifier("type"),
|
'type',
|
||||||
factory.createStringLiteral("varchar")
|
factory.createStringLiteral("enum")
|
||||||
),
|
),
|
||||||
factory.createPropertyAssignment(
|
factory.createPropertyAssignment(
|
||||||
factory.createIdentifier("params"),
|
'enumeration',
|
||||||
factory.createObjectLiteralExpression(
|
factory.createArrayLiteralExpression(
|
||||||
[factory.createPropertyAssignment(
|
enumAttributes[(<ts.Identifier>name).text].map(
|
||||||
factory.createIdentifier("length"),
|
ele => factory.createStringLiteral(ele)
|
||||||
factory.createNumericLiteral(STRING_LITERAL_MAX_LENGTH)
|
)
|
||||||
)],
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -728,6 +728,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
else {
|
else {
|
||||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
|
// 暂时先封掉user上的相关更新条件,会制造出连接表上的update
|
||||||
|
if (entity !== 'user') {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id && Object.keys(filter).length === 1) {
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
Object.assign(otm, {
|
Object.assign(otm, {
|
||||||
|
|
@ -753,6 +755,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// 基于foreignKey的one-to-many
|
// 基于foreignKey的one-to-many
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
|
|
@ -792,6 +795,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||||
|
// 暂时先封掉user上的连接,以避免生成连接表更新
|
||||||
|
if (entity !== 'user') {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id && Object.keys(filter).length === 1) {
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
Object.assign(otm, {
|
Object.assign(otm, {
|
||||||
|
|
@ -808,6 +813,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (action === 'remove' && actionOtm === 'update') {
|
if (action === 'remove' && actionOtm === 'update') {
|
||||||
Object.assign(dataOtm, {
|
Object.assign(dataOtm, {
|
||||||
[foreignKey]: null,
|
[foreignKey]: null,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ 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, SelectOpResult, REMOVE_CASCADE_PRIORITY
|
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY, RefOrExpression, SyncOrAsync
|
||||||
} 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";
|
||||||
|
|
@ -16,13 +16,14 @@ import { generateNewId } from '../utils/uuid';
|
||||||
|
|
||||||
export function translateCheckerInAsyncContext<
|
export function translateCheckerInAsyncContext<
|
||||||
ED extends EntityDict & BaseEntityDict,
|
ED extends EntityDict & BaseEntityDict,
|
||||||
|
T extends keyof ED,
|
||||||
Cxt extends AsyncContext<ED>
|
Cxt extends AsyncContext<ED>
|
||||||
>(checker: Checker<ED, keyof ED, Cxt>): {
|
>(checker: Checker<ED, T, Cxt>): {
|
||||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
fn: Trigger<ED, T, Cxt>['fn'];
|
||||||
when: 'before' | 'after';
|
when: 'before' | 'after';
|
||||||
} {
|
} {
|
||||||
const { entity, type, action } = checker;
|
const { entity, type } = checker;
|
||||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'data': {
|
case 'data': {
|
||||||
const { checker: checkerFn } = checker;
|
const { checker: checkerFn } = checker;
|
||||||
|
|
@ -76,7 +77,7 @@ export function translateCheckerInAsyncContext<
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||||
return {
|
return {
|
||||||
fn,
|
fn,
|
||||||
when,
|
when,
|
||||||
|
|
@ -90,29 +91,19 @@ export function translateCheckerInAsyncContext<
|
||||||
}
|
}
|
||||||
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
||||||
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
||||||
if (operation.action === 'create') {
|
|
||||||
const filter2 = await relationFilter(operation, context, option);
|
|
||||||
|
|
||||||
const { data } = operation as ED[keyof ED]['Create'];
|
const result = typeof relationFilter === 'function' ? await relationFilter(operation, context, option) : relationFilter;
|
||||||
const filter = data instanceof Array ? {
|
|
||||||
id: {
|
if (result) {
|
||||||
$in: data.map(
|
if (operation.action === 'create') {
|
||||||
ele => ele.id,
|
console.warn(`${entity as string}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
||||||
),
|
|
||||||
},
|
|
||||||
} : {
|
|
||||||
id: data.id,
|
|
||||||
};
|
|
||||||
if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, filter, true)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
throw new OakUserUnpermittedException(errMsg);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
|
operation.filter = combineFilters([operation.filter, result as ED[T]['Selection']['filter']]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||||
return {
|
return {
|
||||||
fn,
|
fn,
|
||||||
when,
|
when,
|
||||||
|
|
@ -127,7 +118,7 @@ export function translateCheckerInAsyncContext<
|
||||||
}
|
}
|
||||||
await checkerFn(operation, context, option);
|
await checkerFn(operation, context, option);
|
||||||
return 0;
|
return 0;
|
||||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
}) as UpdateTriggerInTxn<ED, T, Cxt>['fn'];
|
||||||
return {
|
return {
|
||||||
fn,
|
fn,
|
||||||
when,
|
when,
|
||||||
|
|
@ -147,8 +138,8 @@ export function translateCheckerInSyncContext<
|
||||||
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';
|
||||||
} {
|
} {
|
||||||
const { entity, type, action } = checker;
|
const { entity, type } = checker;
|
||||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
const when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'data': {
|
case 'data': {
|
||||||
const { checker: checkerFn } = checker;
|
const { checker: checkerFn } = checker;
|
||||||
|
|
@ -188,23 +179,21 @@ export function translateCheckerInSyncContext<
|
||||||
if (context.isRoot()) {
|
if (context.isRoot()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filter2 = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
const result = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
||||||
|
|
||||||
|
assert(!(result instanceof Promise));
|
||||||
|
if (result) {
|
||||||
const { filter, action } = operation;
|
const { filter, action } = operation;
|
||||||
let filter3 = filter;
|
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
const { data } = operation as ED[T]['Create'];
|
console.warn(`${entity as string}对象的create类型的checker中,存在无法转换为表达式形式的情况,请尽量使用authDef格式定义这类checker`);
|
||||||
filter3 = data instanceof Array ? {
|
return;
|
||||||
id: {
|
|
||||||
$in: data.map(ele => ele.id),
|
|
||||||
},
|
|
||||||
} : { id: data.id };
|
|
||||||
}
|
}
|
||||||
assert(filter3);
|
assert(filter);
|
||||||
assert(!(filter2 instanceof Promise));
|
if (checkFilterContains<ED, T, Cxt>(entity, context, result as ED[T]['Selection']['filter'], filter, true)) {
|
||||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, filter3, true)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new OakUserUnpermittedException(errMsg);
|
throw new OakUserUnpermittedException(errMsg);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
fn,
|
fn,
|
||||||
|
|
@ -231,12 +220,22 @@ export function translateCheckerInSyncContext<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilterMakeFn<ED extends EntityDict & BaseEntityDict> = (operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'], userId: string) => ED[keyof ED]['Selection']['filter'] | {
|
||||||
|
$entity: keyof ED;
|
||||||
|
$filter?: ED[keyof ED]['Selection']['filter'];
|
||||||
|
$count?: number;
|
||||||
|
};
|
||||||
|
|
||||||
function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||||
schema: StorageSchema<ED>,
|
schema: StorageSchema<ED>,
|
||||||
lch: CascadeRelationItem,
|
lch: CascadeRelationItem,
|
||||||
entity2: keyof ED): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
entity2: keyof ED,
|
||||||
|
pathPrefix?: string): FilterMakeFn<ED> {
|
||||||
const { cascadePath, relations } = lch;
|
const { cascadePath, relations } = lch;
|
||||||
const paths = cascadePath.split('.');
|
const paths = cascadePath ? cascadePath.split('.') : [];
|
||||||
|
if (pathPrefix) {
|
||||||
|
paths.unshift(pathPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
const translateRelationFilter = <T extends keyof ED>(entity: T): (userId: string) => ED[T]['Selection']['filter'] => {
|
const translateRelationFilter = <T extends keyof ED>(entity: T): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||||
// 有两种情况,此entity和user有Relation定义,或是此entity已经指向user
|
// 有两种情况,此entity和user有Relation定义,或是此entity已经指向user
|
||||||
|
|
@ -282,11 +281,13 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
||||||
|
|
||||||
const translateFilterMakerIter = <T extends keyof ED>(entity: T, iter: number): (userId: string) => ED[T]['Selection']['filter'] => {
|
const translateFilterMakerIter = <T extends keyof ED>(entity: T, iter: number): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||||
const relation = judgeRelation(schema, entity, paths[iter]);
|
const relation = judgeRelation(schema, entity, paths[iter]);
|
||||||
|
assert(relation === 2 || typeof relation === 'string');
|
||||||
|
|
||||||
if (iter === paths.length - 1) {
|
if (iter === paths.length - 1) {
|
||||||
if (relation === 2) {
|
if (relation === 2) {
|
||||||
const filterMaker = translateRelationFilter(paths[iter]);
|
const filterMaker2 = translateRelationFilter(paths[iter]);
|
||||||
return (userId) => {
|
return (userId) => {
|
||||||
const filter = filterMaker(userId)!;
|
const filter = filterMaker2(userId)!;
|
||||||
assert(filter.id);
|
assert(filter.id);
|
||||||
return {
|
return {
|
||||||
entity: paths[iter],
|
entity: paths[iter],
|
||||||
|
|
@ -294,10 +295,9 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(typeof relation === 'string');
|
const filterMaker2 = translateRelationFilter(relation);
|
||||||
const filterMaker = translateRelationFilter(relation);
|
|
||||||
return (userId) => {
|
return (userId) => {
|
||||||
const filter = filterMaker(userId)!;
|
const filter = filterMaker2(userId)!;
|
||||||
|
|
||||||
assert(filter.id);
|
assert(filter.id);
|
||||||
return {
|
return {
|
||||||
|
|
@ -306,53 +306,265 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const subFilterMaker = translateFilterMakerIter(paths[iter], iter + 1);
|
const filterMaker = relation === 2 ? translateFilterMakerIter(paths[iter], iter + 1) : translateFilterMakerIter(relation, iter + 1);
|
||||||
if (iter === 0) {
|
|
||||||
return (userId) => {
|
|
||||||
const subFilter = subFilterMaker(userId);
|
|
||||||
return {
|
|
||||||
[paths[iter]]: subFilter,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return (userId) => ({
|
return (userId) => ({
|
||||||
[paths[iter]]: subFilterMaker(userId),
|
[paths[iter]]: filterMaker(userId),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
const filterMaker = paths.length ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||||
return filter;
|
if (!paths.length) {
|
||||||
|
return (oper, userId) => filterMaker(userId);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 针对第一层做一下特别优化,比如对象A指向对象B(多对一),如果A的cascadePath是 'B',
|
||||||
|
* 当create A时,会带有Bid。此时生成该B对象上的相关表达式查询返回,可以避免必须将此判定在对象创建之后再做
|
||||||
|
* 另一使用场景是,在查询A时,如果带有Bid(在对象跳一对多子对象场景下很常见),可以提前判定这个查询对某些用户一定返回空集
|
||||||
|
*/
|
||||||
|
const [attr] = paths;
|
||||||
|
const relation = judgeRelation(schema, entity2, attr);
|
||||||
|
assert(relation === 2 || typeof relation === 'string');
|
||||||
|
const filterMaker2 = paths.length > 1
|
||||||
|
? (relation === 2 ? translateFilterMakerIter(attr, 1) : translateFilterMakerIter(relation, 1))
|
||||||
|
: (relation === 2 ? translateRelationFilter(attr) : translateRelationFilter(relation));
|
||||||
|
return (operation, userId) => {
|
||||||
|
const { action } = operation as ED[keyof ED]['Operation'];
|
||||||
|
if (action === 'create') {
|
||||||
|
const { data } = operation as ED[keyof ED]['Create'];
|
||||||
|
const getForeignKeyId = (d: ED[keyof ED]['CreateSingle']['data']) => {
|
||||||
|
if (relation === 2) {
|
||||||
|
if (d.entity === attr && typeof d.entityId === 'string') {
|
||||||
|
return d.entitId as string;
|
||||||
|
}
|
||||||
|
throw new OakUserUnpermittedException();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(typeof relation === 'string');
|
||||||
|
if (typeof d[`${attr}Id`] === 'string') {
|
||||||
|
return d[`${attr}Id`] as string;
|
||||||
|
}
|
||||||
|
throw new OakUserUnpermittedException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (relation === 2) {
|
||||||
|
if (data instanceof Array) {
|
||||||
|
const fkIds = uniq(data.map(d => getForeignKeyId(d)));
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||||
|
$count: fkIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const fkId = getForeignKeyId(data);
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: fkId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
assert(typeof relation === 'string');
|
||||||
|
if (data instanceof Array) {
|
||||||
|
const fkIds = uniq(data.map(d => getForeignKeyId(d)));
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }),
|
||||||
|
$count: fkIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const fkId = getForeignKeyId(data);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: fkId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { filter } = operation;
|
||||||
|
if (relation === 2 && filter?.entity === attr && filter?.entityId) {
|
||||||
|
if (typeof filter.entityId === 'string') {
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: filter.entityId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (filter.entityId.$in && filter.entityId.$in instanceof Array) {
|
||||||
|
const entityIds = uniq(filter.entityId.$in);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||||
|
$count: entityIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filter && filter[`${attr}Id`]) {
|
||||||
|
if (typeof filter[`${attr}Id`] === 'string') {
|
||||||
|
return {
|
||||||
|
$entity: attr,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: filter[`${attr}Id`] }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (filter[`${attr}Id`].$in && filter[`${attr}Id`].$in instanceof Array) {
|
||||||
|
const entityIds = uniq(filter[`${attr}Id`].$in);
|
||||||
|
return {
|
||||||
|
$entity: relation,
|
||||||
|
$filter: addFilterSegment(filterMaker2(userId), { id: { $in: entityIds } }),
|
||||||
|
$count: entityIds.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterMaker(userId);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||||
schema: StorageSchema<ED>,
|
schema: StorageSchema<ED>,
|
||||||
relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
||||||
entity: keyof ED
|
entity: keyof ED,
|
||||||
): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
pathPrefix?: string,
|
||||||
|
): FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[] {
|
||||||
if (relationItem instanceof Array) {
|
if (relationItem instanceof Array) {
|
||||||
const maker = relationItem.map(
|
const maker = relationItem.map(
|
||||||
ele => {
|
ele => {
|
||||||
if (ele instanceof Array) {
|
if (ele instanceof Array) {
|
||||||
return ele.map(
|
return ele.map(
|
||||||
ele2 => translateCascadeRelationFilterMaker(schema, ele2, entity)
|
ele2 => translateCascadeRelationFilterMaker(schema, ele2, entity, pathPrefix)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
return translateCascadeRelationFilterMaker(schema, ele, entity, pathPrefix);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return (userId) => ({
|
return maker;
|
||||||
$or: maker.map(
|
|
||||||
ele => ({
|
|
||||||
$and: ele.map(
|
|
||||||
ele2 => ele2(userId)!
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
const filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
const filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity, pathPrefix);
|
||||||
return (userId) => filterMaker(userId);
|
return filterMaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makePotentialFilter<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>> (
|
||||||
|
operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'],
|
||||||
|
context: Cxt,
|
||||||
|
filterMaker: FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[]): SyncOrAsync<ED[keyof ED]['Selection']['filter']> {
|
||||||
|
const userId = context.getCurrentUserId();
|
||||||
|
assert(userId!);
|
||||||
|
const filters = filterMaker instanceof Array ? filterMaker.map(
|
||||||
|
ele => {
|
||||||
|
if (ele instanceof Array) {
|
||||||
|
return ele.map(
|
||||||
|
ele2 => ele2(operation, userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ele(operation, userId);
|
||||||
|
}
|
||||||
|
) : [filterMaker(operation, userId)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在下面的逻辑中,如果某个maker返回的是$entity类型,则检查是否有满足条件的项,没有就要抛出异常,有就返回undefined
|
||||||
|
* undefined项即意味着该条件通过
|
||||||
|
* 再加上and和or的布尔逻辑判断,得到最终结果
|
||||||
|
* 还要考虑同步和异步……
|
||||||
|
* 代码比较复杂,因为原先没有$entity这种返回结果的设计
|
||||||
|
* by Xc 20130219
|
||||||
|
*/
|
||||||
|
const filtersOr: (SyncOrAsync<ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>>)[] = [];
|
||||||
|
let isAsyncOr = false;
|
||||||
|
for (const f of filters) {
|
||||||
|
if (f instanceof Array) {
|
||||||
|
let isAsyncAnd = true;
|
||||||
|
const filtersAnd: (SyncOrAsync<ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>>)[] = [];
|
||||||
|
for (const ff of f) {
|
||||||
|
if (ff?.$entity) {
|
||||||
|
const { $entity, $filter, $count = 1 } = ff!;
|
||||||
|
const count = context.count($entity, {
|
||||||
|
filter: $filter,
|
||||||
|
}, {});
|
||||||
|
if (count instanceof Promise) {
|
||||||
|
isAsyncAnd = true;
|
||||||
|
filtersAnd.push(
|
||||||
|
count.then(
|
||||||
|
(c2) => {
|
||||||
|
if (c2 >= $count) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return new OakUserUnpermittedException();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filtersAnd.push(count >= $count ? undefined : new OakUserUnpermittedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ff) {
|
||||||
|
filtersAnd.push(ff as ED[keyof ED]['Selection']['filter']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isAsyncAnd = true) {
|
||||||
|
isAsyncOr = true;
|
||||||
|
filtersOr.push(isAsyncAnd ? Promise.all(filtersAnd).then(
|
||||||
|
(fa) => {
|
||||||
|
const faR: ED[keyof ED]['Selection']['filter'][] = [];
|
||||||
|
for (const faItem of fa) {
|
||||||
|
if (faItem instanceof OakUserUnpermittedException) {
|
||||||
|
return faItem;
|
||||||
|
}
|
||||||
|
else if (faItem) {
|
||||||
|
faR.push(faItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (faR.length > 0) {
|
||||||
|
return {
|
||||||
|
$and: faR,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) : ({
|
||||||
|
$and: filtersAnd,
|
||||||
|
} as ED[keyof ED]['Selection']['filter']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (f?.$entity) {
|
||||||
|
const { $entity, $filter, $count = 1 } = f!;
|
||||||
|
const count = context.count($entity, {
|
||||||
|
filter: $filter,
|
||||||
|
}, {});
|
||||||
|
if (count instanceof Promise) {
|
||||||
|
isAsyncOr = true;
|
||||||
|
filtersOr.push(
|
||||||
|
count.then(
|
||||||
|
(c2) => c2 >= $count ? undefined : new OakUserUnpermittedException()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filtersOr.push(count >= $count ? undefined : new OakUserUnpermittedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (f) {
|
||||||
|
filtersOr.push(f as ED[keyof ED]['Selection']['filter']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// or的逻辑是,有一个成功就直接通过
|
||||||
|
const returnOrFilters = (filters: (ED[keyof ED]['Selection']['filter'] | OakUserUnpermittedException<ED>)[]) => {
|
||||||
|
if (filters.length === 0 || filters.includes(undefined)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const foFilters = filters.filter(
|
||||||
|
ele => ele !== undefined && !(ele instanceof OakUserUnpermittedException)
|
||||||
|
);
|
||||||
|
if (foFilters.length > 0) {
|
||||||
|
return {
|
||||||
|
$or: foFilters,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new OakUserUnpermittedException();
|
||||||
|
};
|
||||||
|
if (isAsyncOr) {
|
||||||
|
return Promise.all(filtersOr)
|
||||||
|
.then(
|
||||||
|
(filters) => returnOrFilters(filters)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return returnOrFilters(filtersOr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -370,11 +582,11 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
if (authDict[entity]) {
|
if (authDict[entity]) {
|
||||||
const { relationAuth, actionAuth } = authDict[entity]!;
|
const { relationAuth, actionAuth } = authDict[entity]!;
|
||||||
if (relationAuth) {
|
if (relationAuth) {
|
||||||
const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
|
const raFilterMakerDict = {} as Record<string, FilterMakeFn<ED> | (FilterMakeFn<ED> | FilterMakeFn<ED>[])[]>;
|
||||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||||
for (const r in relationAuth) {
|
for (const r in relationAuth) {
|
||||||
Object.assign(raFilterMakerDict, {
|
Object.assign(raFilterMakerDict, {
|
||||||
[r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
|
[r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, userEntityName, entity),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const entityIdAttr = `${entity}Id`;
|
const entityIdAttr = `${entity}Id`;
|
||||||
|
|
@ -386,14 +598,12 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
const { data } = operation as ED[keyof ED]['Create'];
|
const { data } = operation as ED[keyof ED]['Create'];
|
||||||
assert(!(data instanceof Array));
|
assert(!(data instanceof Array));
|
||||||
const { relation, [entityIdAttr]: entityId } = data;
|
const { relation, [entityIdAttr]: entityId } = data;
|
||||||
const userId = context.getCurrentUserId();
|
|
||||||
if (!raFilterMakerDict[relation]) {
|
if (!raFilterMakerDict[relation]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filter = raFilterMakerDict[relation]!(userId!);
|
const filter = makePotentialFilter(operation, context, raFilterMakerDict[relation]);
|
||||||
return {
|
|
||||||
[entity]: filter,
|
return filter;
|
||||||
};
|
|
||||||
},
|
},
|
||||||
errMsg: '越权操作',
|
errMsg: '越权操作',
|
||||||
});
|
});
|
||||||
|
|
@ -403,26 +613,40 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
action: 'remove' as ED[keyof ED]['Action'],
|
action: 'remove' as ED[keyof ED]['Action'],
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
relationFilter: (operation: any, context: Cxt) => {
|
relationFilter: (operation: any, context: Cxt) => {
|
||||||
const userId = context.getCurrentUserId();
|
// 目前过不去
|
||||||
|
return undefined;
|
||||||
|
/* const userId = context.getCurrentUserId();
|
||||||
const { filter } = operation as ED[keyof ED]['Remove'];
|
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||||
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]) => {
|
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]): SyncOrAsync<ED[keyof ED]['Selection']['filter']> => {
|
||||||
const relations = uniq(rows.map(ele => ele.relation));
|
const relations = uniq(rows.map(ele => ele.relation));
|
||||||
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
|
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
|
||||||
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
|
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
|
||||||
// const entityId = entityIds[0]!;
|
// const entityId = entityIds[0]!;
|
||||||
|
|
||||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||||
return {
|
const filtersAnd = relations.map(
|
||||||
$and: relations.map(
|
|
||||||
(relation) => raFilterMakerDict[relation!]
|
(relation) => raFilterMakerDict[relation!]
|
||||||
).filter(
|
).filter(
|
||||||
ele => !!ele
|
ele => !!ele
|
||||||
).map(
|
).map(
|
||||||
ele => ({
|
ele => makePotentialFilter(operation, context, ele)
|
||||||
[entity]: ele(userId!),
|
);
|
||||||
})
|
if (filtersAnd.find(ele => ele instanceof Promise)) {
|
||||||
)
|
return Promise.all(filtersAnd).then(
|
||||||
|
(fa) => {
|
||||||
|
if (fa.length > 0) {
|
||||||
|
return {
|
||||||
|
$and: fa,
|
||||||
} as ED[keyof ED]['Selection']['filter'];
|
} as ED[keyof ED]['Selection']['filter'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (filtersAnd.length > 0) {
|
||||||
|
return {
|
||||||
|
$and: filtersAnd
|
||||||
|
} as ED[keyof ED]['Selection']['filter'];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toBeRemoved = context.select(userEntityName, {
|
const toBeRemoved = context.select(userEntityName, {
|
||||||
|
|
@ -438,7 +662,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
(rows) => makeFilterFromRows(rows)
|
(rows) => makeFilterFromRows(rows)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return makeFilterFromRows(toBeRemoved);
|
return makeFilterFromRows(toBeRemoved); */
|
||||||
},
|
},
|
||||||
errMsg: '越权操作',
|
errMsg: '越权操作',
|
||||||
});
|
});
|
||||||
|
|
@ -455,7 +679,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
relationFilter: (operation, context) => {
|
relationFilter: (operation, context) => {
|
||||||
// const { filter } = operation;
|
// const { filter } = operation;
|
||||||
const filter = filterMaker(context.getCurrentUserId()!);
|
const filter = makePotentialFilter(operation, context, filterMaker);
|
||||||
return filter;
|
return filter;
|
||||||
},
|
},
|
||||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||||
|
|
@ -538,7 +762,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
[attr]: 1,
|
[attr]: 1,
|
||||||
};
|
};
|
||||||
const filter = operation.filter && {
|
const filter = operation.filter && {
|
||||||
[attr.slice(0, attr.length -2)]: operation.filter
|
[attr.slice(0, attr.length - 2)]: operation.filter
|
||||||
}
|
}
|
||||||
const result = context.select(e, {
|
const result = context.select(e, {
|
||||||
data: proj,
|
data: proj,
|
||||||
|
|
@ -653,7 +877,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
data: {},
|
data: {},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[attr]: filter,
|
[attr]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
}
|
}
|
||||||
return context.operate(entity, {
|
return context.operate(entity, {
|
||||||
|
|
@ -665,7 +889,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
},
|
},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[attr]: filter,
|
[attr]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -687,7 +911,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
data: {},
|
data: {},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[attr]: filter,
|
[attr]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
}
|
}
|
||||||
return context.operate(entity, {
|
return context.operate(entity, {
|
||||||
|
|
@ -698,7 +922,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
},
|
},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[attr]: filter,
|
[attr]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -724,7 +948,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
data: {},
|
data: {},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[e]: filter,
|
[e]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
}
|
}
|
||||||
return context.operate(entity, {
|
return context.operate(entity, {
|
||||||
|
|
@ -736,7 +960,7 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
},
|
},
|
||||||
filter: filter ? {
|
filter: filter ? {
|
||||||
[e]: filter,
|
[e]: filter,
|
||||||
}: undefined,
|
} : undefined,
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,6 @@ export function addFilterSegment<ED extends EntityDict, T extends keyof ED>(...f
|
||||||
filter.$and = ele[k];
|
filter.$and = ele[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (k === '$or') {
|
|
||||||
if (filter.$or) {
|
|
||||||
filter.$or.push(...(ele[k] as any));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filter.$or = ele[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (filter.hasOwnProperty(k)) {
|
else if (filter.hasOwnProperty(k)) {
|
||||||
if (filter.$and) {
|
if (filter.$and) {
|
||||||
filter.$and.push({
|
filter.$and.push({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth, ActionOnRemove } from ".";
|
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth, ActionOnRemove, SyncOrAsync } from ".";
|
||||||
import { AsyncContext } from "../store/AsyncRowStore";
|
import { AsyncContext } from "../store/AsyncRowStore";
|
||||||
import { SyncContext } from "../store/SyncRowStore";
|
import { SyncContext } from "../store/SyncRowStore";
|
||||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||||
|
|
@ -15,9 +15,9 @@ 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) => any | Promise<any>;
|
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => SyncOrAsync<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) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
||||||
entity: T;
|
entity: T;
|
||||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||||
filter: ED[T]['Selection']['filter'] | (
|
filter: ED[T]['Selection']['filter'] | (
|
||||||
(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>
|
(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||||
); // 对行的额外检查条件
|
); // 对行的额外检查条件
|
||||||
errMsg?: string;
|
errMsg?: string;
|
||||||
inconsistentRows?: { // 因为这里的限制不一定在本row上,如果不传这个exception,则默认返回本row上的exception
|
inconsistentRows?: { // 因为这里的限制不一定在本row上,如果不传这个exception,则默认返回本row上的exception
|
||||||
|
|
@ -35,7 +35,7 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
||||||
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
|
||||||
};
|
};
|
||||||
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]['Update']['filter']>
|
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync<ED[T]['Update']['filter']>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -45,10 +45,10 @@ export type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt exten
|
||||||
entity: T;
|
entity: T;
|
||||||
when?: 'after';
|
when?: 'after';
|
||||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'] | Promise<ED[T]['Selection']['filter']>; // 生成一个额外的relation相关的filter,加在原先的filter上
|
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync<ED[T]['Selection']['filter']>, // 生成一个额外的relation相关的filter,加在原先的filter上
|
||||||
errMsg: string;
|
errMsg: string;
|
||||||
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) => SyncOrAsync<ED[T]['Selection']['filter']>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,8 +62,8 @@ 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
|
||||||
) => any | Promise<any>;
|
) => SyncOrAsync<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) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||||
|
|
@ -77,7 +77,7 @@ export type LogicalRelationChecker<ED extends EntityDict, T extends keyof ED, Cx
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
option: OperateOption | SelectOption
|
option: OperateOption | SelectOption
|
||||||
) => any | Promise<any>;
|
) => 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) => SyncOrAsync<ED[T]['Update']['filter']>);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,27 @@ export class OakDeadlock<ED extends EntityDict> extends OakUserException<ED> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class OakPreConditionUnsetException<ED extends EntityDict> extends OakUserException<ED> {
|
||||||
|
entity?: keyof ED;
|
||||||
|
code?: string;
|
||||||
|
|
||||||
|
constructor(message?: string | undefined, entity?: keyof ED | undefined, code?: string | undefined) {
|
||||||
|
super(message || '前置条件不满足');
|
||||||
|
this.entity = entity,
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return JSON.stringify({
|
||||||
|
name: this.constructor.name,
|
||||||
|
message: this.message,
|
||||||
|
code: this.code,
|
||||||
|
entity: this.entity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function makeException<ED extends EntityDict>(data: {
|
export function makeException<ED extends EntityDict>(data: {
|
||||||
name: string;
|
name: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
|
|
@ -303,6 +324,11 @@ export function makeException<ED extends EntityDict>(data: {
|
||||||
e.setOpRecords(data.opRecords);
|
e.setOpRecords(data.opRecords);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
case 'OakPreConditionUnsetException': {
|
||||||
|
const e = new OakPreConditionUnsetException(data.message, data.entity, data.code);
|
||||||
|
e.setOpRecords(data.opRecords);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,6 @@ export type OneOf<Obj> = ValueOf<OneOfByKey<Obj>>;
|
||||||
// 判断对象中的optional key
|
// 判断对象中的optional key
|
||||||
type IsOptional<T, K extends keyof T> = { [K1 in Exclude<keyof T, K>]: T[K1] } & { K?: T[K] } extends T ? K : never
|
type IsOptional<T, K extends keyof T> = { [K1 in Exclude<keyof T, K>]: T[K1] } & { K?: T[K] } extends T ? K : never
|
||||||
export type OptionalKeys<T> = { [K in keyof T]: IsOptional<T, K> }[keyof T]
|
export type OptionalKeys<T> = { [K in keyof T]: IsOptional<T, K> }[keyof T]
|
||||||
|
|
||||||
|
// 同或异步返回
|
||||||
|
export type SyncOrAsync<T> = T | Promise<T>;
|
||||||
|
|
@ -33,6 +33,7 @@ export interface Attribute {
|
||||||
notNull?: boolean;
|
notNull?: boolean;
|
||||||
unique?: boolean;
|
unique?: boolean;
|
||||||
sequenceStart?: number;
|
sequenceStart?: number;
|
||||||
|
enumeration?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Attributes<SH extends EntityShape> = Omit<{
|
export type Attributes<SH extends EntityShape> = Omit<{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { OakInputIllegalException } from "../types";
|
import { EntityDict, OakInputIllegalException } from "../types";
|
||||||
|
|
||||||
type ValidatorFunction = (text: string, size?:number) => string|boolean;
|
type ValidatorFunction = (text: string, size?:number) => string|boolean;
|
||||||
type ValidatorMoneyFunction = (text: string, zero?:boolean) => string|boolean;
|
type ValidatorMoneyFunction = (text: string, zero?:boolean) => string|boolean;
|
||||||
|
|
@ -106,7 +106,7 @@ export const isVehicleNumber: ValidatorFunction = (str) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function checkAttributesNotNull<T extends Record<string, any>>(entity: string, data: T, attributes: Array<keyof T>, allowEmpty?: true) {
|
export function checkAttributesNotNull<ED extends EntityDict, T extends keyof EntityDict>(entity: T, data: Partial<ED[T]['CreateSingle']['data']>, attributes: Array<keyof ED[T]['CreateSingle']['data']>, allowEmpty?: true) {
|
||||||
const attrs = attributes.filter(
|
const attrs = attributes.filter(
|
||||||
(attr) => {
|
(attr) => {
|
||||||
if (data[attr] === null || data[attr] === ''|| data[attr] === undefined) {
|
if (data[attr] === null || data[attr] === ''|| data[attr] === undefined) {
|
||||||
|
|
@ -119,16 +119,16 @@ export function checkAttributesNotNull<T extends Record<string, any>>(entity: st
|
||||||
) as string[];
|
) as string[];
|
||||||
|
|
||||||
if (attrs.length > 0) {
|
if (attrs.length > 0) {
|
||||||
throw new OakInputIllegalException(entity, attrs, '属性不能为空');
|
throw new OakInputIllegalException(entity as string, attrs, '属性不能为空');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function checkAttributesScope<T extends Record<string, any>>(entity: string, data: T, attributes: Array<keyof T>) {
|
export function checkAttributesScope<ED extends EntityDict, T extends keyof EntityDict>(entity: T, data: Partial<ED[T]['CreateSingle']['data']>, attributes: Array<keyof ED[T]['CreateSingle']['data']>) {
|
||||||
const attrs = attributes.filter(
|
const attrs = attributes.filter(
|
||||||
attr => !data.hasOwnProperty(attr)
|
attr => !data.hasOwnProperty(attr)
|
||||||
) as string[];
|
) as string[];
|
||||||
|
|
||||||
if (attrs.length > 0) {
|
if (attrs.length > 0) {
|
||||||
throw new OakInputIllegalException(entity, attrs, '多余的属性');
|
throw new OakInputIllegalException(entity as string, attrs, '多余的属性');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue