Merge branch 'release'
This commit is contained in:
commit
12ca91e37f
|
|
@ -1 +1,5 @@
|
|||
import { CascadeRelationItem, RelationHierarchy } from "../types/Entity";
|
||||
export declare type GenericRelation = 'owner';
|
||||
export declare function convertHierarchyToAuth<R extends string>(hierarchy: RelationHierarchy<R>): {
|
||||
[K in R]?: CascadeRelationItem;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,2 +1,38 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.convertHierarchyToAuth = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
function convertHierarchyToAuth(hierarchy) {
|
||||
var e_1, _a;
|
||||
var _b;
|
||||
var reverseHierarchy = {};
|
||||
for (var r in hierarchy) {
|
||||
try {
|
||||
for (var _c = (e_1 = void 0, tslib_1.__values(hierarchy[r])), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||
var r2 = _d.value;
|
||||
if (reverseHierarchy[r2]) {
|
||||
(_b = reverseHierarchy[r2]) === null || _b === void 0 ? void 0 : _b.push(r);
|
||||
}
|
||||
else {
|
||||
reverseHierarchy[r2] = [r];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
var result = {};
|
||||
for (var r in reverseHierarchy) {
|
||||
result[r] = {
|
||||
cascadePath: '',
|
||||
relations: reverseHierarchy[r],
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.convertHierarchyToAuth = convertHierarchyToAuth;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export declare type SortNode = {
|
|||
export declare type Sorter = SortNode[];
|
||||
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
|
||||
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Schema, Projection, Filter, Sorter>, "id">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Projection, Filter, Sorter>, "id">;
|
||||
export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId">> & ({
|
||||
entity?: string;
|
||||
entityId?: string;
|
||||
|
|
@ -127,10 +127,8 @@ export declare type UpdateOperationData = FormUpdateData<OpSchema> & {
|
|||
export declare type UpdateOperation = OakOperation<"update" | ParticularAction | string, UpdateOperationData, Filter, Sorter>;
|
||||
export declare type RemoveOperationData = {};
|
||||
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type ModiIdSubQuery = Selection<ModiIdProjection>;
|
||||
export declare type NativeAttr = OpAttr;
|
||||
export declare type FullAttr = NativeAttr | `modiEntitys$${number}.${ModiEntity.NativeAttr}` | `operEntitys$${number}.${OperEntity.NativeAttr}`;
|
||||
export declare type EntityDef = {
|
||||
Schema: Schema;
|
||||
OpSchema: OpSchema;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export declare type SortNode = {
|
|||
export declare type Sorter = SortNode[];
|
||||
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
|
||||
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Schema, Projection, Filter, Sorter>, "id">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Projection, Filter, Sorter>, "id">;
|
||||
export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId" | "modiId">> & (({
|
||||
modiId?: never;
|
||||
modi: Modi.CreateSingleOperation;
|
||||
|
|
@ -142,12 +142,10 @@ export declare type RemoveOperationData = {} & (({
|
|||
[k: string]: any;
|
||||
});
|
||||
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type ModiIdSubQuery = Selection<ModiIdProjection>;
|
||||
export declare type UserIdSubQuery = Selection<UserIdProjection>;
|
||||
export declare type ModiEntityIdSubQuery = Selection<ModiEntityIdProjection>;
|
||||
export declare type NativeAttr = OpAttr | `modi.${Modi.NativeAttr}` | `entity.${User.NativeAttr}`;
|
||||
export declare type FullAttr = NativeAttr;
|
||||
export declare type EntityDef = {
|
||||
Schema: Schema;
|
||||
OpSchema: OpSchema;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export declare type SortNode = {
|
|||
export declare type Sorter = SortNode[];
|
||||
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
|
||||
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Schema, Projection, Filter, Sorter>, "id">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Projection, Filter, Sorter>, "id">;
|
||||
export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "operatorId">> & (({
|
||||
operatorId?: never;
|
||||
operator?: User.CreateSingleOperation;
|
||||
|
|
@ -125,11 +125,9 @@ export declare type RemoveOperationData = {} & (({
|
|||
operator?: User.UpdateOperation | User.RemoveOperation;
|
||||
}));
|
||||
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type UserIdSubQuery = Selection<UserIdProjection>;
|
||||
export declare type OperIdSubQuery = Selection<OperIdProjection>;
|
||||
export declare type NativeAttr = OpAttr | `operator.${User.NativeAttr}`;
|
||||
export declare type FullAttr = NativeAttr | `operEntitys$${number}.${OperEntity.NativeAttr}`;
|
||||
export declare type EntityDef = {
|
||||
Schema: Schema;
|
||||
OpSchema: OpSchema;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export declare type SortNode = {
|
|||
export declare type Sorter = SortNode[];
|
||||
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
|
||||
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Schema, Projection, Filter, Sorter>, "id">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Projection, Filter, Sorter>, "id">;
|
||||
export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId" | "operId">> & (({
|
||||
operId?: never;
|
||||
oper: Oper.CreateSingleOperation;
|
||||
|
|
@ -157,13 +157,11 @@ export declare type RemoveOperationData = {} & ({
|
|||
[k: string]: any;
|
||||
});
|
||||
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type OperIdSubQuery = Selection<OperIdProjection>;
|
||||
export declare type ModiIdSubQuery = Selection<ModiIdProjection>;
|
||||
export declare type UserIdSubQuery = Selection<UserIdProjection>;
|
||||
export declare type OperEntityIdSubQuery = Selection<OperEntityIdProjection>;
|
||||
export declare type NativeAttr = OpAttr | `oper.${Oper.NativeAttr}` | `entity.${Modi.NativeAttr}` | `entity.${User.NativeAttr}`;
|
||||
export declare type FullAttr = NativeAttr;
|
||||
export declare type EntityDef = {
|
||||
Schema: Schema;
|
||||
OpSchema: OpSchema;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export declare type SortNode = {
|
|||
export declare type Sorter = SortNode[];
|
||||
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
|
||||
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Schema, Projection, Filter, Sorter>, "id">;
|
||||
export declare type Aggregation = Omit<DeduceAggregation<Projection, Filter, Sorter>, "id">;
|
||||
export declare type CreateOperationData = FormCreateData<OpSchema> & {
|
||||
oper$operator?: OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">[]> | Array<OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">>>;
|
||||
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
|
||||
|
|
@ -110,10 +110,8 @@ export declare type UpdateOperationData = FormUpdateData<OpSchema> & {
|
|||
export declare type UpdateOperation = OakOperation<"update" | RelationAction | string, UpdateOperationData, Filter, Sorter>;
|
||||
export declare type RemoveOperationData = {};
|
||||
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
|
||||
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type UserIdSubQuery = Selection<UserIdProjection>;
|
||||
export declare type NativeAttr = OpAttr;
|
||||
export declare type FullAttr = NativeAttr | `opers$${number}.${Oper.NativeAttr}` | `operEntitys$${number}.${OperEntity.NativeAttr}` | `modiEntitys$${number}.${ModiEntity.NativeAttr}`;
|
||||
export declare type EntityDef = {
|
||||
Schema: Schema;
|
||||
OpSchema: OpSchema;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { EntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
import { SyncContext } from '../store/SyncRowStore';
|
||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types';
|
||||
export declare function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDefDict } from '../types';
|
||||
export declare function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ exports.createDynamicCheckers = void 0;
|
|||
var tslib_1 = require("tslib");
|
||||
var checker_1 = require("../store/checker");
|
||||
var modi_1 = require("../store/modi");
|
||||
function createDynamicCheckers(schema) {
|
||||
function createDynamicCheckers(schema, authDict) {
|
||||
var checkers = [];
|
||||
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
|
||||
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRelationHierarchyCheckers)(schema)), false));
|
||||
if (authDict) {
|
||||
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
|
||||
}
|
||||
return checkers;
|
||||
}
|
||||
exports.createDynamicCheckers = createDynamicCheckers;
|
||||
|
|
|
|||
|
|
@ -337,7 +337,8 @@ function analyzeEntity(filename, path, program, relativePath) {
|
|||
var localEnumStringTypes = [];
|
||||
var additionalImports = [];
|
||||
var localeDef = undefined;
|
||||
var relationHierarchy = undefined;
|
||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
ts.forEachChild(sourceFile, function (node) {
|
||||
var _a, _b, _c, _d;
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
|
|
@ -795,15 +796,22 @@ function analyzeEntity(filename, path, program, relativePath) {
|
|||
_static = true; // static如果有值只能为true
|
||||
}
|
||||
}
|
||||
else if (ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
|
||||
/* else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
|
||||
// RelationHierary
|
||||
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728RelationHierarchy\u4E4B\u540E"));
|
||||
var initializer = declaration.initializer;
|
||||
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress"));
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
relationHierarchy = initializer;
|
||||
}
|
||||
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ReverseCascadeRelationHierarchy') {
|
||||
// ReverseCascadeRelationHierarchy
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在ReverseCascadeRelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
reverseCascadeRelationHierarchy = initializer;
|
||||
} */
|
||||
else {
|
||||
throw new Error("".concat(moduleName, "\uFF1A\u4E0D\u80FD\u7406\u89E3\u7684\u5B9A\u4E49\u5185\u5BB9").concat(declaration.name.getText()));
|
||||
throw new Error("".concat(moduleName, "\uFF1A\u4E0D\u80FD\u7406\u89E3\u7684\u5B9A\u4E49\u5185\u5BB9").concat(declaration.name.text));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -843,15 +851,25 @@ function analyzeEntity(filename, path, program, relativePath) {
|
|||
locale: localeDef,
|
||||
});
|
||||
}
|
||||
if (hasRelationDef) {
|
||||
(0, assert_1.default)(relationHierarchy, "".concat(filename, "\u4E2D\u7F3A\u5C11\u4E86relationHierarchy\u5B9A\u4E49"));
|
||||
(0, lodash_1.assign)(schema, {
|
||||
relationHierarchy: relationHierarchy,
|
||||
});
|
||||
/* if (hasRelationDef) {
|
||||
if(!relationHierarchy && !reverseCascadeRelationHierarchy){
|
||||
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义,请注意自主编写权限分配的checker`);
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
assign(schema, {
|
||||
relationHierarchy,
|
||||
});
|
||||
}
|
||||
if (reverseCascadeRelationHierarchy) {
|
||||
assign(schema, {
|
||||
reverseCascadeRelationHierarchy,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(!relationHierarchy, "".concat(filename, "\u4E2D\u5177\u6709relationHierarchy\u5B9A\u4E49\u4F46\u6CA1\u6709Relation\u5B9A\u4E49"));
|
||||
}
|
||||
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
|
||||
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
|
||||
} */
|
||||
(0, lodash_1.assign)(Schema, (_a = {},
|
||||
_a[moduleName] = schema,
|
||||
_a));
|
||||
|
|
@ -1783,7 +1801,6 @@ function constructActions(statements, entity) {
|
|||
factory.createLiteralTypeNode(factory.createStringLiteral("action"))
|
||||
])), factory.createTypeAliasDeclaration(undefined, [factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("DeduceAggregation"), [
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("Schema"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
|
||||
|
|
@ -1939,6 +1956,11 @@ function constructActions(statements, entity) {
|
|||
var reverseOneNodes = [];
|
||||
if (ReversePointerEntities[entity]) {
|
||||
if (ReversePointerRelations[entity]) {
|
||||
var schemaAttrs = Schema[entity].schemaAttrs;
|
||||
var questionToken = schemaAttrs.find(function (ele) {
|
||||
var name = ele.name;
|
||||
return name.text === 'entity';
|
||||
}).questionToken;
|
||||
try {
|
||||
for (var _q = tslib_1.__values(ReversePointerRelations[entity]), _r = _q.next(); !_r.done; _r = _q.next()) {
|
||||
var one = _r.value;
|
||||
|
|
@ -1956,10 +1978,8 @@ function constructActions(statements, entity) {
|
|||
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), undefined, factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')))
|
||||
]);
|
||||
var noCascadeNode = factory.createTypeLiteralNode([
|
||||
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc
|
||||
factory.createLiteralTypeNode(factory.createStringLiteral("".concat((0, string_1.firstLetterLowerCase)(one))))),
|
||||
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("String"), [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))]))
|
||||
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), questionToken, factory.createLiteralTypeNode(factory.createStringLiteral("".concat((0, string_1.firstLetterLowerCase)(one))))),
|
||||
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), questionToken, factory.createTypeReferenceNode(factory.createIdentifier("String"), [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))]))
|
||||
]);
|
||||
if (Schema[one].static) {
|
||||
reverseOneNodes.push(noCascadeNode);
|
||||
|
|
@ -2584,8 +2604,7 @@ function constructActions(statements, entity) {
|
|||
statements.push(factory.createTypeAliasDeclaration(undefined, [factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Operation"), undefined, factory.createUnionTypeNode([
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("CreateOperation"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperation"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined),
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("SelectOperation"), undefined)
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined)
|
||||
])));
|
||||
}
|
||||
var initialStatements = function () { return [
|
||||
|
|
@ -2642,7 +2661,6 @@ var initialStatements = function () { return [
|
|||
function outputSubQuery(outputDir, printer) {
|
||||
var statements = [];
|
||||
if (process.env.COMPLING_AS_LIB) {
|
||||
statements.push(factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("Selection"))])), factory.createStringLiteral((0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(1)), undefined));
|
||||
}
|
||||
for (var entity in Schema) {
|
||||
// import * as User from '../User/Schema';
|
||||
|
|
@ -2755,7 +2773,8 @@ function outputSchema(outputDir, printer) {
|
|||
constructSorter(statements, entity);
|
||||
constructActions(statements, entity);
|
||||
constructQuery(statements, entity);
|
||||
constructFullAttrs(statements, entity);
|
||||
// 现在FullAttrs和NativeAttrs似乎没什么用,还会引起递归
|
||||
// constructFullAttrs(statements, entity);
|
||||
var makeActionArguments = [];
|
||||
if (ActionAsts[entity]) {
|
||||
makeActionArguments.push(factory.createTypeReferenceNode('Action'));
|
||||
|
|
@ -2787,6 +2806,9 @@ function outputSchema(outputDir, printer) {
|
|||
if (ActionAsts[entity]) {
|
||||
EntityDefAttrs.push(factory.createPropertySignature(undefined, factory.createIdentifier("ParticularAction"), undefined, factory.createTypeReferenceNode(factory.createIdentifier('ParticularAction'), undefined)));
|
||||
}
|
||||
if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef)) {
|
||||
EntityDefAttrs.push(factory.createPropertySignature(undefined, factory.createIdentifier("Relation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Relation'), undefined)));
|
||||
}
|
||||
statements.push(factory.createTypeAliasDeclaration(undefined, [factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("EntityDef"), undefined, factory.createTypeLiteralNode(EntityDefAttrs)));
|
||||
var result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), Schema[entity].sourceFile);
|
||||
var fileName = path_1.default.join(outputDir, entity, 'Schema.ts');
|
||||
|
|
@ -3045,13 +3067,19 @@ function outputStorage(outputDir, printer) {
|
|||
var entityAssignments = [];
|
||||
for (var entity in Schema) {
|
||||
var indexExpressions = [];
|
||||
var _a = Schema[entity], sourceFile = _a.sourceFile, inModi = _a.inModi, indexes = _a.indexes, toModi = _a.toModi, actionType = _a.actionType, _static = _a.static, relationHierarchy = _a.relationHierarchy;
|
||||
var _a = Schema[entity], sourceFile = _a.sourceFile, inModi = _a.inModi, indexes = _a.indexes, toModi = _a.toModi, actionType = _a.actionType, _static = _a.static, hasRelationDef = _a.hasRelationDef;
|
||||
var fromSchemaSpecifiers = [
|
||||
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
|
||||
];
|
||||
if (relationHierarchy) {
|
||||
fromSchemaSpecifiers.push(factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation")));
|
||||
}
|
||||
/* if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
fromSchemaSpecifiers.push(
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("Relation")
|
||||
)
|
||||
);
|
||||
} */
|
||||
var statements = [
|
||||
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StorageDesc"))])), factory.createStringLiteral("".concat((0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(), "Storage")), undefined),
|
||||
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromSchemaSpecifiers)), factory.createStringLiteral("./Schema"), undefined)
|
||||
|
|
@ -3110,15 +3138,43 @@ function outputStorage(outputDir, printer) {
|
|||
if (indexExpressions.length > 0) {
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true)));
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relationHierarchy"), relationHierarchy));
|
||||
/* if (relationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relationHierarchy"),
|
||||
relationHierarchy,
|
||||
)
|
||||
);
|
||||
}
|
||||
if (reverseCascadeRelationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("reverseCascadeRelationHierarchy"),
|
||||
reverseCascadeRelationHierarchy,
|
||||
)
|
||||
);
|
||||
} */
|
||||
if (hasRelationDef) {
|
||||
var type = hasRelationDef.type;
|
||||
if (ts.isUnionTypeNode(type)) {
|
||||
var types = type.types;
|
||||
var relationTexts = types.map(function (ele) {
|
||||
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal));
|
||||
return ele.literal.text;
|
||||
});
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relation"), factory.createArrayLiteralExpression(relationTexts.map(function (ele) { return factory.createStringLiteral(ele); }))));
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(ts.isLiteralTypeNode(type));
|
||||
(0, assert_1.default)(ts.isStringLiteral(type.literal));
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relation"), factory.createArrayLiteralExpression([
|
||||
type.literal
|
||||
])));
|
||||
}
|
||||
}
|
||||
var sdTypeArguments = [
|
||||
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)
|
||||
];
|
||||
if (relationHierarchy) {
|
||||
sdTypeArguments.push(factory.createTypeReferenceNode(factory.createIdentifier("Relation"), undefined));
|
||||
}
|
||||
statements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("desc"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StorageDesc"), sdTypeArguments), factory.createObjectLiteralExpression(propertyAssignments, true))], ts.NodeFlags.Const)));
|
||||
var result_2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile);
|
||||
var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict, OperateOption, SelectOption, OperationResult, DeduceFilter, AggregationResult } from "../types/Entity";
|
||||
import { EntityDict, OperateOption, SelectOption, OperationResult, AggregationResult } from "../types/Entity";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { RowStore } from '../types/RowStore';
|
||||
import { StorageSchema } from '../types/Storage';
|
||||
|
|
@ -51,14 +51,14 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
|||
* @param filter
|
||||
* @returns
|
||||
*/
|
||||
protected destructCascadeUpdate<T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>, OP extends OperateOption, R>(entity: T, action: ED[T]['Action'], data: ED[T]['CreateSingle']['data'] | ED[T]['Update']['data'] | ED[T]['Remove']['data'], context: Cxt, option: OP, cascadeUpdate: <T2 extends keyof ED>(entity: T2, operation: ED[T2]['Operation'], context: Cxt, option: OP) => R, filter?: DeduceFilter<ED[T]['Schema']>): {
|
||||
protected destructCascadeUpdate<T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>, OP extends OperateOption, R>(entity: T, action: ED[T]['Action'], data: ED[T]['CreateSingle']['data'] | ED[T]['Update']['data'] | ED[T]['Remove']['data'], context: Cxt, option: OP, cascadeUpdate: <T2 extends keyof ED>(entity: T2, operation: ED[T2]['Operation'], context: Cxt, option: OP) => R, filter?: ED[T]['Update']['filter']): {
|
||||
data: Record<string, any>;
|
||||
beforeFns: (() => R)[];
|
||||
afterFns: (() => R)[];
|
||||
};
|
||||
protected preProcessDataCreated<T extends keyof ED>(entity: T, data: ED[T]['Create']['data']): void;
|
||||
protected preProcessDataUpdated<T extends keyof ED>(data: ED[T]['Update']['data']): void;
|
||||
judgeRelation(entity: keyof ED, attr: string): string | 2 | 1 | string[] | 0;
|
||||
protected preProcessDataUpdated(data: Record<string, any>): void;
|
||||
judgeRelation(entity: keyof ED, attr: string): string | 1 | 2 | string[] | 0;
|
||||
/**
|
||||
* 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
|
||||
* @param entity
|
||||
|
|
|
|||
|
|
@ -32,15 +32,15 @@ var TriggerExecutor = /** @class */ (function () {
|
|||
TriggerExecutor.prototype.registerChecker = function (checker) {
|
||||
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
|
||||
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
|
||||
var fn = (0, checker_1.translateCheckerInAsyncContext)(checker);
|
||||
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
|
||||
var trigger = {
|
||||
checkerType: type,
|
||||
name: triggerName,
|
||||
priority: checker.priority || 2,
|
||||
priority: checker.priority || 20,
|
||||
entity: entity,
|
||||
action: action,
|
||||
fn: fn,
|
||||
when: 'before',
|
||||
when: when,
|
||||
filter: conditionalFilter,
|
||||
};
|
||||
this.registerTrigger(trigger);
|
||||
|
|
@ -59,7 +59,7 @@ var TriggerExecutor = /** @class */ (function () {
|
|||
throw new Error("\u4E0D\u53EF\u6709\u540C\u540D\u7684\u89E6\u53D1\u5668\u300C".concat(trigger.name, "\u300D"));
|
||||
}
|
||||
if (typeof trigger.priority !== 'number') {
|
||||
trigger.priority = 1; // 默认最低
|
||||
trigger.priority = 10; // 默认值
|
||||
}
|
||||
if (trigger.filter) {
|
||||
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
|
||||
|
|
@ -205,6 +205,7 @@ var TriggerExecutor = /** @class */ (function () {
|
|||
(0, assert_1.default)(operation.action !== 'create');
|
||||
var filter = trigger.filter;
|
||||
var filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
(0, assert_1.default)(!(filterr instanceof Promise));
|
||||
var filterRepelled = (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
|
||||
if (filterRepelled) {
|
||||
continue;
|
||||
|
|
@ -228,28 +229,37 @@ var TriggerExecutor = /** @class */ (function () {
|
|||
else {
|
||||
// 异步context
|
||||
var execPreTrigger_1 = function (idx) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var trigger, filter, filterr, filterRepelled, number;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
var trigger, filter, filterr, _a, filterRepelled, number;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (idx >= preTriggers_2.length) {
|
||||
return [2 /*return*/];
|
||||
}
|
||||
trigger = preTriggers_2[idx];
|
||||
if (!trigger.filter) return [3 /*break*/, 2];
|
||||
if (!trigger.filter) return [3 /*break*/, 5];
|
||||
(0, assert_1.default)(operation.action !== 'create');
|
||||
filter = trigger.filter;
|
||||
filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)];
|
||||
if (!(typeof filter === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, filter(operation, context, option)];
|
||||
case 1:
|
||||
filterRepelled = _a.sent();
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
_a = filter;
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
filterr = _a;
|
||||
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)];
|
||||
case 4:
|
||||
filterRepelled = _b.sent();
|
||||
if (filterRepelled) {
|
||||
return [2 /*return*/, execPreTrigger_1(idx + 1)];
|
||||
}
|
||||
_a.label = 2;
|
||||
case 2: return [4 /*yield*/, trigger.fn({ operation: operation }, context, option)];
|
||||
case 3:
|
||||
number = _a.sent();
|
||||
_b.label = 5;
|
||||
case 5: return [4 /*yield*/, trigger.fn({ operation: operation }, context, option)];
|
||||
case 6:
|
||||
number = _b.sent();
|
||||
if (number > 0) {
|
||||
this.logger.info("\u89E6\u53D1\u5668\u300C".concat(trigger.name, "\u300D\u6210\u529F\u89E6\u53D1\u4E86\u300C").concat(number, "\u300D\u884C\u6570\u636E\u66F4\u6539"));
|
||||
}
|
||||
|
|
@ -258,28 +268,37 @@ var TriggerExecutor = /** @class */ (function () {
|
|||
});
|
||||
}); };
|
||||
var execCommitTrigger_1 = function (idx) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var trigger, filter, filterr, filterRepelled;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
var trigger, filter, filterr, _a, filterRepelled;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (idx >= commitTriggers_1.length) {
|
||||
return [2 /*return*/];
|
||||
}
|
||||
trigger = commitTriggers_1[idx];
|
||||
if (!trigger.filter) return [3 /*break*/, 2];
|
||||
if (!trigger.filter) return [3 /*break*/, 5];
|
||||
(0, assert_1.default)(operation.action !== 'create');
|
||||
filter = trigger.filter;
|
||||
filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)];
|
||||
if (!(typeof filter === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, filter(operation, context, option)];
|
||||
case 1:
|
||||
filterRepelled = _a.sent();
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
_a = filter;
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
filterr = _a;
|
||||
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)];
|
||||
case 4:
|
||||
filterRepelled = _b.sent();
|
||||
if (filterRepelled) {
|
||||
return [2 /*return*/, execCommitTrigger_1(idx + 1)];
|
||||
}
|
||||
_a.label = 2;
|
||||
case 2: return [4 /*yield*/, this.preCommitTrigger(entity, operation, trigger, context, option)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_b.label = 5;
|
||||
case 5: return [4 /*yield*/, this.preCommitTrigger(entity, operation, trigger, context, option)];
|
||||
case 6:
|
||||
_b.sent();
|
||||
return [2 /*return*/, execCommitTrigger_1(idx + 1)];
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import { Checker, EntityDict, OperateOption, SelectOption, StorageSchema, Trigger } from "../types";
|
||||
import { AuthDefDict, Checker, EntityDict, OperateOption, SelectOption, StorageSchema, Trigger } from "../types";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from "./AsyncRowStore";
|
||||
import { SyncContext } from './SyncRowStore';
|
||||
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn'];
|
||||
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
||||
export declare function createRelationHierarchyCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): {
|
||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
||||
when: 'before' | 'after';
|
||||
};
|
||||
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
||||
fn: (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
||||
when: 'before' | 'after';
|
||||
};
|
||||
export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
|
|
|
|||
|
|
@ -1,58 +1,79 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createRelationHierarchyCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
||||
exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var filter_1 = require("../store/filter");
|
||||
var Exception_1 = require("../types/Exception");
|
||||
var actionDef_1 = require("./actionDef");
|
||||
var string_1 = require("../utils/string");
|
||||
var lodash_1 = require("../utils/lodash");
|
||||
var relation_1 = require("./relation");
|
||||
function translateCheckerInAsyncContext(checker) {
|
||||
var _this = this;
|
||||
var entity = checker.entity, type = checker.type;
|
||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
var checkerFn_1 = checker.checker;
|
||||
return (function (_a, context) {
|
||||
var fn = (function (_a, context) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var data;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
data = operation.data;
|
||||
checkerFn_1(data, context);
|
||||
return [2 /*return*/, 0];
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
data = operation.data;
|
||||
return [4 /*yield*/, checkerFn_1(data, context)];
|
||||
case 1:
|
||||
_b.sent();
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'row': {
|
||||
var filter_2 = checker.filter, errMsg_1 = checker.errMsg, inconsistentRows_1 = checker.inconsistentRows;
|
||||
return (function (_a, context, option) {
|
||||
var fn = (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var operationFilter, action, filter2, entity2, selection2, rows2, data_1, rows2, data_2;
|
||||
var _b, _c;
|
||||
return tslib_1.__generator(this, function (_d) {
|
||||
switch (_d.label) {
|
||||
var operationFilter, action, filter2, _b, entity2, selection2, rows2, data_1, rows2, data_2;
|
||||
var _c, _d;
|
||||
return tslib_1.__generator(this, function (_e) {
|
||||
switch (_e.label) {
|
||||
case 0:
|
||||
operationFilter = operation.filter, action = operation.action;
|
||||
filter2 = typeof filter_2 === 'function' ? filter_2(operation, context, option) : filter_2;
|
||||
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 1];
|
||||
if (!(typeof filter_2 === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, filter_2(operation, context, option)];
|
||||
case 1:
|
||||
_b = _e.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
_b = filter_2;
|
||||
_e.label = 3;
|
||||
case 3:
|
||||
filter2 = _b;
|
||||
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 4];
|
||||
operation.filter = (0, filter_1.addFilterSegment)(operationFilter || {}, filter2);
|
||||
return [2 /*return*/, 0];
|
||||
case 1: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {})];
|
||||
case 2:
|
||||
if (_d.sent()) {
|
||||
case 4: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)];
|
||||
case 5:
|
||||
if (_e.sent()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
if (!inconsistentRows_1) return [3 /*break*/, 4];
|
||||
if (!inconsistentRows_1) return [3 /*break*/, 7];
|
||||
entity2 = inconsistentRows_1.entity, selection2 = inconsistentRows_1.selection;
|
||||
return [4 /*yield*/, context.select(entity2, selection2(operationFilter), {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
})];
|
||||
case 3:
|
||||
rows2 = _d.sent();
|
||||
case 6:
|
||||
rows2 = _e.sent();
|
||||
data_1 = {};
|
||||
rows2.forEach(function (ele) {
|
||||
var _a;
|
||||
|
|
@ -62,11 +83,11 @@ function translateCheckerInAsyncContext(checker) {
|
|||
});
|
||||
throw new Exception_1.OakRowInconsistencyException({
|
||||
a: 's',
|
||||
d: (_b = {},
|
||||
_b[entity2] = data_1,
|
||||
_b)
|
||||
d: (_c = {},
|
||||
_c[entity2] = data_1,
|
||||
_c)
|
||||
}, errMsg_1);
|
||||
case 4: return [4 /*yield*/, context.select(entity, {
|
||||
case 7: return [4 /*yield*/, context.select(entity, {
|
||||
data: (0, actionDef_1.getFullProjection)(entity, context.getSchema()),
|
||||
filter: Object.assign({}, operationFilter, {
|
||||
$not: filter2,
|
||||
|
|
@ -75,8 +96,8 @@ function translateCheckerInAsyncContext(checker) {
|
|||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
})];
|
||||
case 5:
|
||||
rows2 = _d.sent();
|
||||
case 8:
|
||||
rows2 = _e.sent();
|
||||
data_2 = {};
|
||||
rows2.forEach(function (ele) {
|
||||
var _a;
|
||||
|
|
@ -86,70 +107,91 @@ function translateCheckerInAsyncContext(checker) {
|
|||
});
|
||||
throw new Exception_1.OakRowInconsistencyException({
|
||||
a: 's',
|
||||
d: (_c = {},
|
||||
_c[entity] = data_2,
|
||||
_c)
|
||||
d: (_d = {},
|
||||
_d[entity] = data_2,
|
||||
_d)
|
||||
}, errMsg_1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'relation': {
|
||||
var relationFilter_1 = checker.relationFilter;
|
||||
return (function (_a, context, option) {
|
||||
var relationFilter_1 = checker.relationFilter, errMsg_2 = checker.errMsg;
|
||||
var fn = (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var filter2, data, filter, _b, _c, _d;
|
||||
return tslib_1.__generator(this, function (_e) {
|
||||
switch (_e.label) {
|
||||
case 0:
|
||||
if (context.isRoot()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
if (!(operation.action === 'create')) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
||||
case 1:
|
||||
filter2 = _e.sent();
|
||||
data = operation.data;
|
||||
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:
|
||||
if (_e.sent()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_2);
|
||||
case 3:
|
||||
_b = operation;
|
||||
_c = filter_1.combineFilters;
|
||||
_d = [operation.filter];
|
||||
return [4 /*yield*/, relationFilter_1(operation, context, option)];
|
||||
case 4:
|
||||
_b.filter = _c.apply(void 0, [_d.concat([_e.sent()])]);
|
||||
_e.label = 5;
|
||||
case 5: return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'logical':
|
||||
case 'logicalRelation': {
|
||||
var checkerFn_2 = checker.checker;
|
||||
var fn = (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
if (context.isRoot()) {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
||||
operation.filter = (0, filter_1.combineFilters)([operation.filter, relationFilter_1(operation, context, option)]);
|
||||
return [2 /*return*/, 0];
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
case 'expression':
|
||||
case 'expressionRelation': {
|
||||
var expression_1 = checker.expression, errMsg_2 = checker.errMsg;
|
||||
return (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var exprResult, expressionEntity, expr, expressionFilter, _b, result;
|
||||
return tslib_1.__generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (context.isRoot() && type === 'expressionRelation') {
|
||||
if (context.isRoot() && type === 'logicalRelation') {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
exprResult = expression_1(operation, context, option);
|
||||
if (!(typeof exprResult === 'string')) return [3 /*break*/, 1];
|
||||
throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_2);
|
||||
return [4 /*yield*/, checkerFn_2(operation, context, option)];
|
||||
case 1:
|
||||
if (!(exprResult === undefined)) return [3 /*break*/, 2];
|
||||
_b.sent();
|
||||
return [2 /*return*/, 0];
|
||||
case 2:
|
||||
expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
|
||||
return [4 /*yield*/, context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true }))];
|
||||
case 3:
|
||||
_b = tslib_1.__read.apply(void 0, [_c.sent(), 1]), result = _b[0];
|
||||
if (!result) {
|
||||
// 条件判定为假,抛异常
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_2);
|
||||
}
|
||||
_c.label = 4;
|
||||
case 4: return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
(0, assert_1.default)(false);
|
||||
|
|
@ -158,15 +200,20 @@ function translateCheckerInAsyncContext(checker) {
|
|||
}
|
||||
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
|
||||
function translateCheckerInSyncContext(checker) {
|
||||
var entity = checker.entity, type = checker.type;
|
||||
var entity = checker.entity, type = checker.type, action = checker.action;
|
||||
var when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before';
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
var checkerFn_2 = checker.checker;
|
||||
return function (operation, context) { return checkerFn_2(operation.data, context); };
|
||||
var checkerFn_3 = checker.checker;
|
||||
var fn = function (operation, context) { return checkerFn_3(operation.data, context); };
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'row': {
|
||||
var filter_3 = checker.filter, errMsg_3 = checker.errMsg;
|
||||
return function (operation, context, option) {
|
||||
var fn = function (operation, context, option) {
|
||||
var operationFilter = operation.filter, action = operation.action;
|
||||
var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3;
|
||||
(0, assert_1.default)(operationFilter);
|
||||
|
|
@ -175,56 +222,59 @@ function translateCheckerInSyncContext(checker) {
|
|||
return 0;
|
||||
}
|
||||
else {
|
||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter)) {
|
||||
(0, assert_1.default)(!(filter2 instanceof Promise));
|
||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
|
||||
return;
|
||||
}
|
||||
throw new Exception_1.OakRowInconsistencyException(undefined, errMsg_3);
|
||||
}
|
||||
};
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'relation': {
|
||||
var filter_4 = checker.relationFilter, errMsg_4 = checker.errMsg;
|
||||
return function (operation, context, option) {
|
||||
var relationFilter_2 = checker.relationFilter, errMsg_4 = checker.errMsg;
|
||||
var fn = function (operation, context, option) {
|
||||
if (context.isRoot()) {
|
||||
return;
|
||||
}
|
||||
var filter2 = typeof filter_4 === 'function' ? filter_4(operation, context, option) : filter_4;
|
||||
var operationFilter = operation.filter;
|
||||
(0, assert_1.default)(operationFilter);
|
||||
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter)) {
|
||||
var filter2 = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
|
||||
var filter = operation.filter, action = operation.action;
|
||||
var filter3 = filter;
|
||||
if (action === 'create') {
|
||||
var data = operation.data;
|
||||
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;
|
||||
}
|
||||
throw new Exception_1.OakUserUnpermittedException(errMsg_4);
|
||||
};
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
case 'expression':
|
||||
case 'expressionRelation': {
|
||||
var expression_2 = checker.expression, errMsg_5 = checker.errMsg;
|
||||
return function (operation, context, option) {
|
||||
if (context.isRoot() && type === 'expressionRelation') {
|
||||
return;
|
||||
}
|
||||
var exprResult = expression_2(operation, context, option);
|
||||
if (typeof exprResult === 'string') {
|
||||
throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_5);
|
||||
}
|
||||
else if (exprResult === undefined) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
var expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
|
||||
var _a = tslib_1.__read(context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true })), 1), result = _a[0];
|
||||
if (!result.$expr) {
|
||||
// 条件判定为假,抛异常
|
||||
throw new Exception_1.OakRowInconsistencyException(undefined, errMsg_5);
|
||||
}
|
||||
case 'logical':
|
||||
case 'logicalRelation': {
|
||||
var checkerFn_4 = checker.checker;
|
||||
var fn = function (operation, context, option) {
|
||||
if (context.isRoot() && type === 'logicalRelation') {
|
||||
return;
|
||||
}
|
||||
checkerFn_4(operation, context, option);
|
||||
};
|
||||
return {
|
||||
fn: fn,
|
||||
when: when,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
|
|
@ -233,154 +283,211 @@ function translateCheckerInSyncContext(checker) {
|
|||
}
|
||||
}
|
||||
exports.translateCheckerInSyncContext = translateCheckerInSyncContext;
|
||||
function createRelationHierarchyCheckers(schema) {
|
||||
function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
||||
var cascadePath = lch.cascadePath, relations = lch.relations;
|
||||
var paths = cascadePath.split('.');
|
||||
var translateRelationFilter = function (entity) {
|
||||
// 有两种情况,此entity和user有Relation定义,或是此entity上有userId
|
||||
if (schema[entity].relation) {
|
||||
var relationEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var filter = relations ? {
|
||||
userId: userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
} : {
|
||||
userId: userId,
|
||||
};
|
||||
return {
|
||||
id: {
|
||||
$in: {
|
||||
entity: relationEntityName_1,
|
||||
data: (_a = {},
|
||||
_a["".concat(entity, "Id")] = 1,
|
||||
_a),
|
||||
filter: filter,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
var attributes = schema[entity].attributes;
|
||||
(0, assert_1.default)(attributes.hasOwnProperty('userId') && attributes.userId.type === 'ref' && attributes.userId.ref === 'user', "\u5728".concat(entity, "\u4E0A\u65E2\u627E\u4E0D\u5230userId\uFF0C\u4E5F\u6CA1\u6709relation\u5B9A\u4E49"));
|
||||
return function (userId) { return ({
|
||||
userId: userId,
|
||||
}); };
|
||||
};
|
||||
var translateFilterMakerIter = function (entity, iter) {
|
||||
var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]);
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
var filterMaker_1 = translateRelationFilter(paths[iter]);
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var filter = filterMaker_1(userId);
|
||||
if (filter.$in) {
|
||||
return {
|
||||
entity: paths[iter],
|
||||
entityId: filter,
|
||||
};
|
||||
}
|
||||
return _a = {},
|
||||
_a[paths[iter]] = filter,
|
||||
_a;
|
||||
};
|
||||
}
|
||||
(0, assert_1.default)(typeof relation === 'string');
|
||||
var filterMaker_2 = translateRelationFilter(relation);
|
||||
return function (userId) {
|
||||
var _a, _b;
|
||||
var filter = filterMaker_2(userId);
|
||||
if (filter.$in) {
|
||||
return _a = {},
|
||||
_a["".concat(paths[iter], "Id")] = filter,
|
||||
_a;
|
||||
}
|
||||
return _b = {},
|
||||
_b[paths[iter]] = filter,
|
||||
_b;
|
||||
};
|
||||
}
|
||||
else {
|
||||
var subFilterMaker_1 = translateFilterMakerIter(paths[iter], 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) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[paths[iter]] = subFilterMaker_1(userId),
|
||||
_a);
|
||||
};
|
||||
}
|
||||
};
|
||||
var filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
}
|
||||
function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
||||
if (relationItem instanceof Array) {
|
||||
var maker_1 = relationItem.map(function (ele) {
|
||||
if (ele instanceof Array) {
|
||||
return ele.map(function (ele2) { return translateCascadeRelationFilterMaker(schema, ele2, entity); });
|
||||
}
|
||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
||||
});
|
||||
return function (userId) { return ({
|
||||
$or: maker_1.map(function (ele) { return ({
|
||||
$and: ele.map(function (ele2) { return ele2(userId); })
|
||||
}); })
|
||||
}); };
|
||||
}
|
||||
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||
return function (userId) { return filterMaker(userId); };
|
||||
}
|
||||
function createAuthCheckers(schema, authDict) {
|
||||
var checkers = [];
|
||||
var _loop_1 = function (entity) {
|
||||
var e_1, _a;
|
||||
var relationHierarchy = schema[entity].relationHierarchy;
|
||||
if (relationHierarchy) {
|
||||
// 先build反向hierarchy的map
|
||||
var reverseHierarchy_1 = {};
|
||||
for (var r in relationHierarchy) {
|
||||
try {
|
||||
for (var _b = (e_1 = void 0, tslib_1.__values(relationHierarchy[r])), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var r2 = _c.value;
|
||||
if (!reverseHierarchy_1[r2]) {
|
||||
reverseHierarchy_1[r2] = [r];
|
||||
}
|
||||
else {
|
||||
reverseHierarchy_1[r2].push(r);
|
||||
}
|
||||
}
|
||||
var _a;
|
||||
if (authDict[entity]) {
|
||||
var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
|
||||
if (relationAuth) {
|
||||
var raFilterMakerDict_1 = {};
|
||||
for (var r in relationAuth) {
|
||||
Object.assign(raFilterMakerDict_1, (_a = {},
|
||||
_a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
|
||||
_a));
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
// 对userEntity对象的授权和回收建立checker
|
||||
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||
var entityIdAttr_1 = "".concat(entity, "Id");
|
||||
checkers.push({
|
||||
entity: userEntityName_1,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
expression: function (operation, context) {
|
||||
var _a;
|
||||
var data = operation.data;
|
||||
var _b = data, relation = _b.relation, _c = entityIdAttr_1, entityId = _b[_c];
|
||||
var legalRelations = reverseHierarchy_1[relation];
|
||||
if (!legalRelations) {
|
||||
return undefined;
|
||||
}
|
||||
if (legalRelations.length === 0) {
|
||||
return '这是不应该跑出来的情况,请杀程序员祭天';
|
||||
}
|
||||
var userId = context.getCurrentUserId();
|
||||
return {
|
||||
entity: userEntityName_1,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
filter: (_a = {
|
||||
userId: userId
|
||||
},
|
||||
_a[entityIdAttr_1] = entityId,
|
||||
_a.relation = {
|
||||
$in: legalRelations,
|
||||
},
|
||||
_a)
|
||||
};
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
var _loop_2 = function (r) {
|
||||
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||
var entityIdAttr_1 = "".concat(entity, "Id");
|
||||
checkers.push({
|
||||
entity: userEntityName_1,
|
||||
action: 'remove',
|
||||
type: 'expressionRelation',
|
||||
conditionalFilter: {
|
||||
relation: r,
|
||||
},
|
||||
expression: function (operation, context) {
|
||||
var _a, _b;
|
||||
action: 'create',
|
||||
type: 'relation',
|
||||
relationFilter: function (operation, context) {
|
||||
var _a;
|
||||
var data = operation.data;
|
||||
(0, assert_1.default)(!(data instanceof Array));
|
||||
var _b = data, relation = _b.relation, _c = entityIdAttr_1, entityId = _b[_c];
|
||||
var userId = context.getCurrentUserId();
|
||||
var filter = operation.filter;
|
||||
var legalRelations = reverseHierarchy_1[r];
|
||||
if (legalRelations.length === 0) {
|
||||
return '这是不应该跑出来的情况,请杀程序员祭天';
|
||||
if (!raFilterMakerDict_1[relation]) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
entity: userEntityName_1,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
filter: (_a = {
|
||||
userId: userId
|
||||
},
|
||||
_a[entityIdAttr_1] = {
|
||||
$in: {
|
||||
entity: userEntityName_1,
|
||||
data: (_b = {},
|
||||
_b[entityIdAttr_1] = 1,
|
||||
_b),
|
||||
filter: filter,
|
||||
}
|
||||
},
|
||||
_a.relation = {
|
||||
$in: legalRelations,
|
||||
},
|
||||
_a),
|
||||
};
|
||||
var filter = raFilterMakerDict_1[relation](userId);
|
||||
return _a = {},
|
||||
_a[entity] = filter,
|
||||
_a;
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
};
|
||||
for (var r in reverseHierarchy_1) {
|
||||
_loop_2(r);
|
||||
}
|
||||
/* // 一个人不能授权给自己,也不能删除自己的授权
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'create' as ED[keyof ED]['Action'],
|
||||
type: 'data',
|
||||
checker: (data, context) => {
|
||||
assert(!(data instanceof Array));
|
||||
const { userId } = data as ED[keyof ED]['CreateSingle']['data'];
|
||||
const userId2 = context.getCurrentUserId(true);
|
||||
if (userId === userId2) {
|
||||
throw new OakDataException('不允许授权给自己');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'remove' as ED[keyof ED]['Action'],
|
||||
type: 'row',
|
||||
filter: (operation, context) => {
|
||||
const userId = context.getCurrentUserId(true);
|
||||
if (userId) {
|
||||
return {
|
||||
userId: {
|
||||
$ne: userId,
|
||||
},
|
||||
checkers.push({
|
||||
entity: userEntityName_1,
|
||||
action: 'remove',
|
||||
type: 'relation',
|
||||
relationFilter: function (operation, context) {
|
||||
var _a;
|
||||
var userId = context.getCurrentUserId();
|
||||
var filter = operation.filter;
|
||||
var makeFilterFromRows = function (rows) {
|
||||
var relations = (0, lodash_1.uniq)(rows.map(function (ele) { return ele.relation; }));
|
||||
var entityIds = (0, lodash_1.uniq)(rows.map(function (ele) { return ele[entityIdAttr_1]; }));
|
||||
(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 entityId = entityIds[0]!;
|
||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||
return {
|
||||
$and: relations.map(function (relation) { return raFilterMakerDict_1[relation]; }).filter(function (ele) { return !!ele; }).map(function (ele) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[entity] = ele(userId),
|
||||
_a);
|
||||
})
|
||||
};
|
||||
};
|
||||
}
|
||||
console.warn(`没有当前用户但在删除权限,请检查。对象是${entity}`);
|
||||
return {};
|
||||
},
|
||||
errMsg: '不允许回收自己的授权',
|
||||
}); */
|
||||
// 转让权限现在用update动作,只允许update userId给其它人
|
||||
// todo 等实现的时候再写
|
||||
var toBeRemoved = context.select(userEntityName_1, {
|
||||
data: (_a = {
|
||||
id: 1,
|
||||
relation: 1
|
||||
},
|
||||
_a[entityIdAttr_1] = 1,
|
||||
_a),
|
||||
filter: filter,
|
||||
}, { dontCollect: true });
|
||||
if (toBeRemoved instanceof Promise) {
|
||||
return toBeRemoved.then(function (rows) { return makeFilterFromRows(rows); });
|
||||
}
|
||||
return makeFilterFromRows(toBeRemoved);
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
// 转让权限现在用update动作,只允许update userId给其它人
|
||||
// todo 等实现的时候再写
|
||||
}
|
||||
if (actionAuth) {
|
||||
var _loop_2 = function (a) {
|
||||
var filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a], entity);
|
||||
checkers.push({
|
||||
entity: entity,
|
||||
action: a,
|
||||
type: 'relation',
|
||||
relationFilter: function (operation, context) {
|
||||
// const { filter } = operation;
|
||||
var filter = filterMaker(context.getCurrentUserId());
|
||||
return filter;
|
||||
},
|
||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||
});
|
||||
};
|
||||
for (var a in actionAuth) {
|
||||
_loop_2(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (var entity in schema) {
|
||||
|
|
@ -388,4 +495,4 @@ function createRelationHierarchyCheckers(schema) {
|
|||
}
|
||||
return checkers;
|
||||
}
|
||||
exports.createRelationHierarchyCheckers = createRelationHierarchyCheckers;
|
||||
exports.createAuthCheckers = createAuthCheckers;
|
||||
|
|
|
|||
|
|
@ -96,5 +96,14 @@ export declare function makeTreeAncestorFilter<ED extends EntityDict, T extends
|
|||
* @param level
|
||||
*/
|
||||
export declare function makeTreeDescendantFilter<ED extends EntityDict, T extends keyof ED>(entity: T, parentKey: string, filter: ED[T]['Selection']['filter'], level?: number, includeAll?: boolean, includeSelf?: boolean): ED[T]['Selection']['filter'];
|
||||
export declare function checkFilterContains<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, contained: ED[T]['Selection']['filter'], filter?: ED[T]['Selection']['filter']): boolean | Promise<boolean>;
|
||||
export declare function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, filter1: ED[T]['Selection']['filter'], filter2: ED[T]['Selection']['filter']): boolean | Promise<boolean>;
|
||||
/**
|
||||
* 检查filter是否包含contained(filter查询的数据一定满足contained)
|
||||
* @param entity
|
||||
* @param context
|
||||
* @param contained
|
||||
* @param filter
|
||||
* @param dataCompare
|
||||
* @returns
|
||||
*/
|
||||
export declare function checkFilterContains<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, contained: ED[T]['Selection']['filter'], filter?: ED[T]['Selection']['filter'], dataCompare?: true): boolean | Promise<boolean>;
|
||||
export declare function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, filter1: ED[T]['Selection']['filter'], filter2: ED[T]['Selection']['filter'], dataCompare?: true): boolean | Promise<boolean>;
|
||||
|
|
|
|||
|
|
@ -828,7 +828,16 @@ function makeTreeDescendantFilter(entity, parentKey, filter, level, includeAll,
|
|||
return currentLevelInFilter;
|
||||
}
|
||||
exports.makeTreeDescendantFilter = makeTreeDescendantFilter;
|
||||
function checkFilterContains(entity, context, contained, filter) {
|
||||
/**
|
||||
* 检查filter是否包含contained(filter查询的数据一定满足contained)
|
||||
* @param entity
|
||||
* @param context
|
||||
* @param contained
|
||||
* @param filter
|
||||
* @param dataCompare
|
||||
* @returns
|
||||
*/
|
||||
function checkFilterContains(entity, context, contained, filter, dataCompare) {
|
||||
if (!filter) {
|
||||
throw new types_1.OakRowInconsistencyException();
|
||||
}
|
||||
|
|
@ -837,23 +846,26 @@ function checkFilterContains(entity, context, contained, filter) {
|
|||
if (contains(entity, schema, filter, contained)) {
|
||||
return true;
|
||||
}
|
||||
// 再判断加上了conditionalFilter后取得的行数是否缩减
|
||||
var filter2 = combineFilters([filter, {
|
||||
$not: contained,
|
||||
}]);
|
||||
var count = context.count(entity, {
|
||||
filter: filter2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(function (count2) { return count2 === 0; });
|
||||
if (dataCompare) {
|
||||
// 再判断加上了conditionalFilter后取得的行数是否缩减
|
||||
var filter2 = combineFilters([filter, {
|
||||
$not: contained,
|
||||
}]);
|
||||
var count = context.count(entity, {
|
||||
filter: filter2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(function (count2) { return count2 === 0; });
|
||||
}
|
||||
return count === 0;
|
||||
}
|
||||
return count === 0;
|
||||
return false;
|
||||
}
|
||||
exports.checkFilterContains = checkFilterContains;
|
||||
function checkFilterRepel(entity, context, filter1, filter2) {
|
||||
function checkFilterRepel(entity, context, filter1, filter2, dataCompare) {
|
||||
if (!filter2) {
|
||||
throw new types_1.OakRowInconsistencyException();
|
||||
}
|
||||
|
|
@ -863,16 +875,19 @@ function checkFilterRepel(entity, context, filter1, filter2) {
|
|||
return true;
|
||||
}
|
||||
// 再判断两者同时成立时取得的行数是否为0
|
||||
var filter3 = combineFilters([filter2, filter1]);
|
||||
var count = context.count(entity, {
|
||||
filter: filter3,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(function (count2) { return count2 === 0; });
|
||||
if (dataCompare) {
|
||||
var filter3 = combineFilters([filter2, filter1]);
|
||||
var count = context.count(entity, {
|
||||
filter: filter3,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(function (count2) { return count2 === 0; });
|
||||
}
|
||||
return count === 0;
|
||||
}
|
||||
return count === 0;
|
||||
return false;
|
||||
}
|
||||
exports.checkFilterRepel = checkFilterRepel;
|
||||
|
|
|
|||
|
|
@ -58,14 +58,6 @@ function abandonModis(filter, context, option) {
|
|||
_d.action = 'abandon',
|
||||
_d.data = {},
|
||||
_d.filter = filter,
|
||||
_d.sorter = [
|
||||
{
|
||||
$attr: {
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
$direction: 'asc',
|
||||
}
|
||||
],
|
||||
_d), Object.assign({}, option, {
|
||||
blockTrigger: false,
|
||||
})]))];
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ import { StorageSchema } from "../types/Storage";
|
|||
*/
|
||||
export declare function judgeRelation<ED extends {
|
||||
[E: string]: EntityDef;
|
||||
}>(schema: StorageSchema<ED>, entity: keyof ED, attr: string): string | 2 | 1 | string[] | 0;
|
||||
}>(schema: StorageSchema<ED>, entity: keyof ED, attr: string): string | 1 | 2 | string[] | 0;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ function judgeRelation(schema, entity, attr) {
|
|||
return 2;
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(Entity_1.initinctiveAttributes.includes(attr), "".concat(attr, "\u5C5E\u6027\u627E\u4E0D\u5230"));
|
||||
(0, assert_1.default)(Entity_1.initinctiveAttributes.includes(attr), "".concat(entity, "\u5BF9\u8C61\u4E2D\u7684").concat(attr, "\u5C5E\u6027\u627E\u4E0D\u5230"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { EntityDict } from "./Entity";
|
||||
import { CascadeRelationItem, EntityDict } from "./Entity";
|
||||
import { GenericAction } from '../actions/action';
|
||||
export declare type Action = string;
|
||||
export declare type State = string;
|
||||
export declare type ActionDef<A extends Action, S extends State> = {
|
||||
|
|
@ -12,3 +13,6 @@ export declare type ActionDictOfEntityDict<E extends EntityDict> = {
|
|||
[A in keyof E[T]['OpSchema']]?: ActionDef<string, string>;
|
||||
};
|
||||
};
|
||||
export declare type CascadeActionAuth<A extends Action = ''> = {
|
||||
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { CascadeActionAuth, CascadeRelationAuth } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||
import { RefOrExpression } from "./Expression";
|
||||
export declare type CheckerType = 'relation' | 'row' | 'data' | 'expression' | 'expressionRelation';
|
||||
export declare type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation';
|
||||
/**
|
||||
* conditionalFilter是指该action发生时,operation所操作的行中有满足conditionalFilter的行
|
||||
* 被转化成trigger的filter条件,详细可看trigger中的说明
|
||||
|
|
@ -12,55 +12,55 @@ export declare type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt e
|
|||
type: 'data';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||
};
|
||||
export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'row';
|
||||
entity: T;
|
||||
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']);
|
||||
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']>);
|
||||
errMsg?: string;
|
||||
inconsistentRows?: {
|
||||
entity: keyof ED;
|
||||
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']);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>);
|
||||
};
|
||||
export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'relation';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter'];
|
||||
when?: 'after';
|
||||
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']>;
|
||||
errMsg: string;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
|
||||
};
|
||||
export declare type ExpressionChecker<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;
|
||||
type: 'expression';
|
||||
type: 'logical';
|
||||
when?: 'after';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
expression: <T2 extends keyof ED>(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
entity: T2;
|
||||
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
|
||||
filter: ED[T2]['Selection']['filter'];
|
||||
} | undefined | string;
|
||||
errMsg: string;
|
||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
};
|
||||
export declare type ExpressionRelationChecker<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;
|
||||
type: 'expressionRelation';
|
||||
type: 'logicalRelation';
|
||||
when?: 'after';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
expression: <T2 extends keyof ED>(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
entity: T2;
|
||||
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
|
||||
filter: ED[T2]['Selection']['filter'];
|
||||
} | undefined | string;
|
||||
errMsg: string;
|
||||
checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
};
|
||||
export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<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> = {
|
||||
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||
};
|
||||
export declare type AuthDefDict<ED extends EntityDict> = {
|
||||
[K in keyof ED]?: AuthDef<ED, K>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
/// <reference types="node" />
|
||||
import { IncomingHttpHeaders, IncomingMessage } from "http";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { EntityDict } from "./Entity";
|
||||
export interface Endpoint<ED extends EntityDict, BackCxt extends AsyncContext<ED>> {
|
||||
name: string;
|
||||
params?: string[];
|
||||
method: 'get' | 'post' | 'put' | 'delete';
|
||||
fn: (context: BackCxt, params: Record<string, string>, headers: IncomingHttpHeaders, req: IncomingMessage, body?: any) => Promise<any>;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
;
|
||||
|
|
@ -1,6 +1,3 @@
|
|||
import { GenericAction } from '../actions/action';
|
||||
import { ExprOp, MakeFilter, NodeId } from './Demand';
|
||||
import { OneOf } from './Polyfill';
|
||||
import { PrimaryKey, Sequence } from './DataType';
|
||||
declare type TriggerDataAttributeType = '$$triggerData$$';
|
||||
declare type TriggerTimestampAttributeType = '$$triggerTimestamp$$';
|
||||
|
|
@ -18,7 +15,7 @@ export declare const DeleteAtAttribute = "$$deleteAt$$";
|
|||
export declare const SeqAttribute = "$$seq$$";
|
||||
export declare type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType | DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType;
|
||||
export declare const initinctiveAttributes: string[];
|
||||
export declare type Filter<A extends string, F extends Object | undefined = undefined> = {
|
||||
declare type FilterPart<A extends string, F extends Object | undefined> = {
|
||||
filter?: A extends 'create' ? undefined : F;
|
||||
indexFrom?: A extends 'create' ? undefined : number;
|
||||
count?: A extends 'create' ? undefined : number;
|
||||
|
|
@ -44,16 +41,15 @@ export declare type OperateOption = {
|
|||
export declare type FormUpdateData<SH extends GeneralEntityShape> = Partial<{
|
||||
[K in keyof Omit<SH, InstinctiveAttributes>]: SH[K] | null;
|
||||
}>;
|
||||
export declare type FormCreateData<SH extends GeneralEntityShape> = Omit<SH, InstinctiveAttributes> & {
|
||||
export declare type FormCreateData<SH extends GeneralEntityShape> = Partial<Omit<SH, InstinctiveAttributes>> & {
|
||||
id: string;
|
||||
};
|
||||
export declare type Operation<A extends GenericAction | string, DATA extends Object, FILTER extends Object | undefined = undefined, SORTER extends Object | undefined = undefined> = {
|
||||
export declare type Operation<A extends string, D extends Projection, F extends Filter | undefined = undefined, S extends Sorter | undefined = undefined> = {
|
||||
id?: string;
|
||||
action: A;
|
||||
data: DATA;
|
||||
sorter?: SORTER;
|
||||
} & Filter<A, FILTER>;
|
||||
export declare type Selection<DATA extends Object, FILTER extends Object | undefined = undefined, SORT extends Object | undefined = undefined> = Operation<'select', DATA, FILTER, SORT>;
|
||||
data: D;
|
||||
sorter?: S;
|
||||
} & FilterPart<A, F>;
|
||||
export interface EntityShape {
|
||||
id: PrimaryKey;
|
||||
$$seq$$: Sequence;
|
||||
|
|
@ -61,8 +57,6 @@ export interface EntityShape {
|
|||
$$updateAt$$: number | Date;
|
||||
$$deleteAt$$?: number | Date | null;
|
||||
}
|
||||
export interface FileCarrierEntityShape extends EntityShape {
|
||||
}
|
||||
interface GeneralEntityShape extends EntityShape {
|
||||
[K: string]: any;
|
||||
}
|
||||
|
|
@ -72,28 +66,24 @@ export interface EntityDef {
|
|||
OpSchema: GeneralEntityShape;
|
||||
Action: string;
|
||||
ParticularAction?: string;
|
||||
Selection: Omit<DeduceSelection<this['Schema']>, 'action'>;
|
||||
Aggregation: Omit<DeduceAggregation<this['Schema'], DeduceProjection<this['Schema']>, DeduceFilter<this['Schema']>, DeduceSorter<this['Schema']>>, 'action'>;
|
||||
Operation: DeduceOperation<this['Schema']>;
|
||||
Create: DeduceCreateOperation<this['Schema']>;
|
||||
CreateSingle: DeduceCreateSingleOperation<this['Schema']>;
|
||||
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
|
||||
Update: DeduceUpdateOperation<this['Schema']>;
|
||||
Remove: DeduceRemoveOperation<this['Schema']>;
|
||||
Selection: Omit<Operation<'select', Projection, Filter, Sorter>, 'action'>;
|
||||
Aggregation: Omit<DeduceAggregation<Projection, Filter, Sorter>, 'action'>;
|
||||
Operation: CUDOperation;
|
||||
Create: CreateOperation;
|
||||
CreateSingle: CreateSingleOperation;
|
||||
CreateMulti: CreateMultipleOperation;
|
||||
Update: UpdateOperation;
|
||||
Remove: RemoveOperation;
|
||||
Relation?: string;
|
||||
}
|
||||
export interface EntityDict {
|
||||
[E: string]: EntityDef;
|
||||
}
|
||||
export interface OtmSubProjection extends Omit<DeduceSelection<any>, 'action'> {
|
||||
export interface OtmSubProjection extends Omit<Operation<'select', any, any, any>, 'action'> {
|
||||
$entity: string;
|
||||
}
|
||||
declare type DeduceProjection<SH extends GeneralEntityShape> = {
|
||||
'#id'?: NodeId;
|
||||
} & {
|
||||
[K in keyof SH]?: number | OtmSubProjection | any;
|
||||
} & Partial<ExprOp<keyof SH | string>>;
|
||||
export declare type AggregationOp = `#max-${number}` | `#min-${number}` | `#avg-${number}` | `#count-${number}` | `#sum-${number}`;
|
||||
export declare type DeduceAggregationData<SH extends GeneralEntityShape, P extends DeduceProjection<SH>> = {
|
||||
export declare type DeduceAggregationData<P extends Projection> = {
|
||||
[A in AggregationOp]?: P;
|
||||
} & {
|
||||
'#aggr'?: P;
|
||||
|
|
@ -104,36 +94,40 @@ export declare type AggregationResult<SH extends GeneralEntityShape> = Array<{
|
|||
'#data'?: Partial<SH>;
|
||||
}>;
|
||||
export declare type AttrFilter<SH extends GeneralEntityShape> = {
|
||||
[K in keyof SH]: any;
|
||||
[K in keyof SH]?: any;
|
||||
};
|
||||
export declare type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>;
|
||||
export declare type DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{
|
||||
[K: string]: number | object | undefined;
|
||||
} & ExprOp<keyof SH>>;
|
||||
export declare type DeduceSorterItem<SH extends GeneralEntityShape> = {
|
||||
$attr: DeduceSorterAttr<SH>;
|
||||
declare type SortAttr = {
|
||||
[K: string]: any;
|
||||
};
|
||||
declare type SorterItem = {
|
||||
$attr: SortAttr;
|
||||
$direction?: "asc" | "desc";
|
||||
};
|
||||
export declare type DeduceSorter<SH extends GeneralEntityShape> = Array<DeduceSorterItem<SH>>;
|
||||
export declare type DeduceSelection<SH extends GeneralEntityShape> = Selection<DeduceProjection<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
export declare type DeduceAggregation<SH extends GeneralEntityShape, P extends DeduceProjection<SH>, F extends DeduceFilter<SH>, S extends DeduceSorter<SH>> = Omit<Operation<'aggregate', DeduceAggregationData<SH, P>, F, S>, 'action'>;
|
||||
export declare type DeduceCreateOperationData<SH extends GeneralEntityShape> = {
|
||||
declare type Sorter = Array<SorterItem>;
|
||||
declare type Filter = {
|
||||
[K: string]: any;
|
||||
};
|
||||
declare type Projection = {
|
||||
[K: string]: any;
|
||||
};
|
||||
export declare type DeduceAggregation<P extends Projection, F extends Filter, S extends Sorter> = Omit<Operation<'aggregate', DeduceAggregationData<P>, F, S>, 'action'>;
|
||||
declare type CreateOperationData = {
|
||||
id: string;
|
||||
} & {
|
||||
[k in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
[K: string]: any;
|
||||
};
|
||||
export declare type DeduceCreateSingleOperation<SH extends GeneralEntityShape> = Operation<'create', DeduceCreateOperationData<SH>>;
|
||||
export declare type DeduceCreateMultipleOperation<SH extends GeneralEntityShape> = Operation<'create', Array<DeduceCreateOperationData<SH>>>;
|
||||
export declare type DeduceCreateOperation<SH extends GeneralEntityShape> = DeduceCreateSingleOperation<SH> | DeduceCreateMultipleOperation<SH>;
|
||||
export declare type DeduceUpdateOperationData<SH extends GeneralEntityShape> = {
|
||||
[k in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
declare type CreateSingleOperation = Operation<'create', CreateOperationData, undefined, undefined>;
|
||||
declare type CreateMultipleOperation = Operation<'create', Array<CreateOperationData>, undefined, undefined>;
|
||||
declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
declare type UpdateOperationData = {
|
||||
id?: never;
|
||||
[k: string]: any;
|
||||
};
|
||||
export declare type DeduceUpdateOperation<SH extends GeneralEntityShape> = Operation<'update' | string, DeduceUpdateOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
export declare type DeduceRemoveOperationData<SH extends GeneralEntityShape> = {
|
||||
[A in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
export declare type UpdateOperation = Operation<string, UpdateOperationData, Filter, Sorter>;
|
||||
declare type RemoveOperationData = {
|
||||
[k: string]: any;
|
||||
};
|
||||
export declare type DeduceRemoveOperation<SH extends GeneralEntityShape> = Operation<'remove', DeduceRemoveOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
export declare type DeduceOperation<SH extends GeneralEntityShape> = DeduceCreateOperation<SH> | DeduceUpdateOperation<SH> | DeduceRemoveOperation<SH>;
|
||||
export declare type RemoveOperation = Operation<'remove', RemoveOperationData, Filter, Sorter>;
|
||||
export declare type CUDOperation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
export declare type CreateOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'c';
|
||||
e: T;
|
||||
|
|
@ -142,17 +136,24 @@ export declare type CreateOpResult<ED extends EntityDict, T extends keyof ED> =
|
|||
export declare type UpdateOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'u';
|
||||
e: T;
|
||||
d: DeduceUpdateOperationData<ED[T]['Schema']>;
|
||||
f?: DeduceFilter<ED[T]['Schema']>;
|
||||
d: UpdateOperationData;
|
||||
f?: Filter;
|
||||
};
|
||||
export declare type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'r';
|
||||
e: T;
|
||||
f?: DeduceFilter<ED[T]['Schema']>;
|
||||
f?: Filter;
|
||||
};
|
||||
export declare type RelationHierarchy<R extends string> = {
|
||||
[K in R]?: R[];
|
||||
};
|
||||
export declare type CascadeRelationItem = {
|
||||
cascadePath: string;
|
||||
relations?: string[];
|
||||
};
|
||||
export declare type CascadeRelationAuth<R extends string> = {
|
||||
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
export declare type SelectOpResult<ED extends EntityDict> = {
|
||||
a: 's';
|
||||
d: {
|
||||
|
|
@ -172,19 +173,4 @@ export declare type Configuration = {
|
|||
actionType?: ActionType;
|
||||
static?: boolean;
|
||||
};
|
||||
export declare type Exportation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
projection: ED[T]['Selection']['data'];
|
||||
headers: K[];
|
||||
fn: (data: ED[T]['Schema']) => Partial<Record<K, string | number | boolean | null>>;
|
||||
};
|
||||
export declare type Importation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
headers: K[];
|
||||
fn: (data: Partial<Record<K, string | number | boolean>>) => ED[T]['CreateSingle']['data'];
|
||||
};
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -12,4 +12,3 @@ exports.initinctiveAttributes = [exports.PrimaryKeyAttribute, exports.TriggerDat
|
|||
;
|
||||
;
|
||||
;
|
||||
;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ export declare class OakException extends Error {
|
|||
}
|
||||
export declare class OakDataException extends OakException {
|
||||
}
|
||||
export declare class OakImportDataParseException extends OakException {
|
||||
line: number;
|
||||
header?: string;
|
||||
constructor(message: string, line: number, header?: string);
|
||||
}
|
||||
export declare class OakOperExistedException extends OakDataException {
|
||||
}
|
||||
export declare class OakRowUnexistedException extends OakDataException {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
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.OakDataException = exports.OakException = void 0;
|
||||
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.OakDataException = exports.OakException = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var OakException = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(OakException, _super);
|
||||
|
|
@ -36,6 +36,18 @@ var OakDataException = /** @class */ (function (_super) {
|
|||
return OakDataException;
|
||||
}(OakException));
|
||||
exports.OakDataException = OakDataException;
|
||||
var OakImportDataParseException = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(OakImportDataParseException, _super);
|
||||
// message必传,描述具体错误的数据内容
|
||||
function OakImportDataParseException(message, line, header) {
|
||||
var _this = _super.call(this, message) || this;
|
||||
_this.line = line;
|
||||
_this.header = header;
|
||||
return _this;
|
||||
}
|
||||
return OakImportDataParseException;
|
||||
}(OakException));
|
||||
exports.OakImportDataParseException = OakImportDataParseException;
|
||||
var OakOperExistedException = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(OakOperExistedException, _super);
|
||||
function OakOperExistedException() {
|
||||
|
|
@ -245,6 +257,9 @@ function makeException(data) {
|
|||
case 'OakDeadlock': {
|
||||
return new OakDeadlock(data.message);
|
||||
}
|
||||
case 'OakImportDataParseException': {
|
||||
return new OakImportDataParseException(data.message, data.line, data.header);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export declare type RefOrExpression<A> = RefAttr<A> | Expression<A>;
|
|||
declare type MathType<A> = RefOrExpression<A> | number;
|
||||
declare type StringType<A> = RefOrExpression<A> | string;
|
||||
interface Add<A> {
|
||||
$add: (MathType<A> | StringType<A>)[];
|
||||
$add: (MathType<A>)[];
|
||||
}
|
||||
interface Subtract<A> {
|
||||
$subtract: [MathType<A>, MathType<A>];
|
||||
|
|
@ -111,6 +111,10 @@ interface DateFloor<A> {
|
|||
$dateFloor: [RefOrExpression<A> | Date | number, 'y' | 'M' | 'd' | 'h' | 'm' | 's'];
|
||||
}
|
||||
declare type DateExpression<A> = DateYear<A> | DateMonth<A> | DateWeekday<A> | DateWeekOfYear<A> | DateDay<A> | DateDayOfYear<A> | DateDayOfMonth<A> | DateDayOfWeek<A> | DateDiff<A> | DateCeiling<A> | DateFloor<A>;
|
||||
interface StringConcat<A> {
|
||||
$concat: StringType<A>[];
|
||||
}
|
||||
declare type StringExpression<A> = StringConcat<A>;
|
||||
interface GeoContains<A> {
|
||||
$contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
|
||||
}
|
||||
|
|
@ -118,7 +122,23 @@ interface GeoDistance<A> {
|
|||
$distance: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
|
||||
}
|
||||
declare type GeoExpression<A> = GeoContains<A> | GeoDistance<A>;
|
||||
export declare type Expression<A> = GeoExpression<A> | DateExpression<A> | LogicExpression<A> | BoolExpression<A> | CompareExpression<A> | MathExpression<A>;
|
||||
interface AggrCountExpression<A> {
|
||||
$$count: RefOrExpression<A>;
|
||||
}
|
||||
interface AggrSumExpression<A> {
|
||||
$$sum: RefOrExpression<A>;
|
||||
}
|
||||
interface AggrMaxExpression<A> {
|
||||
$$max: RefOrExpression<A>;
|
||||
}
|
||||
interface AggrMinExpression<A> {
|
||||
$$min: RefOrExpression<A>;
|
||||
}
|
||||
interface AggrAvgExpression<A> {
|
||||
$$avg: RefOrExpression<A>;
|
||||
}
|
||||
export declare type AggrExpression<A> = AggrAvgExpression<A> | AggrCountExpression<A> | AggrSumExpression<A> | AggrMaxExpression<A> | AggrMinExpression<A>;
|
||||
export declare type Expression<A> = GeoExpression<A> | DateExpression<A> | LogicExpression<A> | BoolExpression<A> | CompareExpression<A> | MathExpression<A> | StringExpression<A> | AggrExpression<A>;
|
||||
export declare type ExpressionConstant = Geo | number | Date | string | boolean;
|
||||
export declare function isGeoExpression<A>(expression: any): expression is GeoExpression<A>;
|
||||
export declare function isDateExpression<A>(expression: any): expression is DateExpression<A>;
|
||||
|
|
@ -126,6 +146,8 @@ export declare function isLogicExpression<A>(expression: any): expression is Log
|
|||
export declare function isBoolExpression<A>(expression: any): expression is BoolExpression<A>;
|
||||
export declare function isCompareExpression<A>(expression: any): expression is CompareExpression<A>;
|
||||
export declare function isMathExpression<A>(expression: any): expression is MathExpression<A>;
|
||||
export declare function isStringExpression<A>(expression: any): expression is StringExpression<A>;
|
||||
export declare function isAggrExpression<A>(expression: any): expression is AggrExpression<A>;
|
||||
export declare function isExpression<A>(expression: any): expression is Expression<A>;
|
||||
export declare function opMultipleParams(op: string): boolean;
|
||||
export declare function execOp(op: string, params: any, obscure?: boolean): ExpressionConstant;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getAttrRefInExpression = exports.execOp = exports.opMultipleParams = exports.isExpression = exports.isMathExpression = exports.isCompareExpression = exports.isBoolExpression = exports.isLogicExpression = exports.isDateExpression = exports.isGeoExpression = void 0;
|
||||
exports.getAttrRefInExpression = exports.execOp = exports.opMultipleParams = exports.isExpression = exports.isAggrExpression = exports.isStringExpression = exports.isMathExpression = exports.isCompareExpression = exports.isBoolExpression = exports.isLogicExpression = exports.isDateExpression = exports.isGeoExpression = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
||||
|
|
@ -42,6 +42,7 @@ dayjs_1.default.extend(dayOfYear_1.default);
|
|||
;
|
||||
;
|
||||
;
|
||||
;
|
||||
function isGeoExpression(expression) {
|
||||
if (Object.keys(expression).length == 1) {
|
||||
var op = Object.keys(expression)[0];
|
||||
|
|
@ -105,13 +106,34 @@ function isMathExpression(expression) {
|
|||
return false;
|
||||
}
|
||||
exports.isMathExpression = isMathExpression;
|
||||
function isStringExpression(expression) {
|
||||
if (Object.keys(expression).length == 1) {
|
||||
var op = Object.keys(expression)[0];
|
||||
if (['$concat'].includes(op)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.isStringExpression = isStringExpression;
|
||||
function isAggrExpression(expression) {
|
||||
if (Object.keys(expression).length == 1) {
|
||||
var op = Object.keys(expression)[0];
|
||||
if (['$$max', '$$min', '$$sum', '$$avg', '$$count'].includes(op)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.isAggrExpression = isAggrExpression;
|
||||
function isExpression(expression) {
|
||||
return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$');
|
||||
}
|
||||
exports.isExpression = isExpression;
|
||||
function opMultipleParams(op) {
|
||||
return !['$year', '$month', '$weekday', '$weekOfYear', '$day', '$dayOfMonth',
|
||||
'$dayOfWeek', '$dayOfYear', '$not', '$true', '$false', '$abs', '$round', '$floor', '$ceil'].includes(op);
|
||||
'$dayOfWeek', '$dayOfYear', '$not', '$true', '$false', '$abs',
|
||||
'$round', '$floor', '$ceil', '$$max', '$$min', '$$sum', '$$avg', '$$count'].includes(op);
|
||||
}
|
||||
exports.opMultipleParams = opMultipleParams;
|
||||
function execOp(op, params, obscure) {
|
||||
|
|
@ -341,6 +363,9 @@ function execOp(op, params, obscure) {
|
|||
case '$contains': {
|
||||
throw new Error('$contains类型未实现');
|
||||
}
|
||||
case '$concat': {
|
||||
return params.join('');
|
||||
}
|
||||
default: {
|
||||
(0, assert_1.default)(false, "\u4E0D\u80FD\u8BC6\u522B\u7684expression\u8FD0\u7B97\u7B26\uFF1A".concat(op));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { EntityDict } from "./Entity";
|
||||
export declare type Exportation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
projection: ED[T]['Selection']['data'];
|
||||
headers: K[];
|
||||
fn: (data: ED[T]['Schema']) => Partial<Record<K, string | number | boolean | null>>;
|
||||
};
|
||||
export declare type Importation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
headers: K[];
|
||||
fn: (data: Partial<Record<K, string | number | boolean>>[], context: AsyncContext<ED>, option?: Record<string, any>) => Promise<ED[T]['CreateMulti']['data']>;
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { ActionType } from '.';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy } from './Entity';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes } from './Entity';
|
||||
import { DataType, DataTypeParams } from './schema/DataTypes';
|
||||
export declare type Ref = 'ref';
|
||||
export interface Column<SH extends EntityShape> {
|
||||
|
|
@ -36,7 +36,7 @@ export declare type UniqConstraint<SH extends EntityShape> = {
|
|||
attributes: Array<keyof SH>;
|
||||
type?: string;
|
||||
};
|
||||
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> {
|
||||
export interface StorageDesc<SH extends EntityShape> {
|
||||
storageName?: string;
|
||||
comment?: string;
|
||||
attributes: Attributes<SH>;
|
||||
|
|
@ -48,9 +48,9 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
|
|||
static?: true;
|
||||
actions: string[];
|
||||
actionType: ActionType;
|
||||
relationHierarchy?: RelationHierarchy<Relation>;
|
||||
relation?: string[];
|
||||
view?: true;
|
||||
}
|
||||
export declare type StorageSchema<ED extends EntityDict> = {
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>;
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export interface UpdateTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
|
|||
fn: (event: {
|
||||
operation: ED[T]['Update'];
|
||||
}, context: Cxt, option: OperateOption) => Promise<number> | number;
|
||||
filter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Update'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
filter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Update'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>);
|
||||
}
|
||||
export interface UpdateTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends UpdateTriggerBase<ED, T, Cxt> {
|
||||
when: 'before' | 'after';
|
||||
|
|
@ -58,7 +58,7 @@ export interface RemoveTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
|
|||
fn: (event: {
|
||||
operation: ED[T]['Remove'];
|
||||
}, context: Cxt, option: OperateOption) => Promise<number> | number;
|
||||
filter?: ED[T]['Remove']['filter'] | ((operation: ED[T]['Remove'], context: Cxt, option: OperateOption) => ED[T]['Remove']['filter']);
|
||||
filter?: ED[T]['Remove']['filter'] | ((operation: ED[T]['Remove'], context: Cxt, option: OperateOption) => ED[T]['Remove']['filter'] | Promise<ED[T]['Remove']['filter']>);
|
||||
}
|
||||
export interface RemoveTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt> {
|
||||
when: 'before' | 'after';
|
||||
|
|
|
|||
|
|
@ -17,3 +17,5 @@ export * from './Watcher';
|
|||
export * from './AppLoader';
|
||||
export * from './Connector';
|
||||
export * from './Timer';
|
||||
export * from './Port';
|
||||
export * from './Endpoint';
|
||||
|
|
|
|||
|
|
@ -20,3 +20,5 @@ tslib_1.__exportStar(require("./Watcher"), exports);
|
|||
tslib_1.__exportStar(require("./AppLoader"), exports);
|
||||
tslib_1.__exportStar(require("./Connector"), exports);
|
||||
tslib_1.__exportStar(require("./Timer"), exports);
|
||||
tslib_1.__exportStar(require("./Port"), exports);
|
||||
tslib_1.__exportStar(require("./Endpoint"), exports);
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export declare function excelStringToDate(str: string | number): number | undefined;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.excelStringToDate = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
||||
function excelStringToDate(str) {
|
||||
if (!str) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof str === 'number') {
|
||||
if (str < 100000) {
|
||||
return (0, dayjs_1.default)((((str - 25569) * 24 - 8) * 3600) * 1000).valueOf(); // excel日期可能为1900-1-1至今的天数
|
||||
}
|
||||
return (0, dayjs_1.default)(str).valueOf();
|
||||
}
|
||||
return Date.parse(str);
|
||||
}
|
||||
exports.excelStringToDate = excelStringToDate;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oak-domain",
|
||||
"version": "2.3.2",
|
||||
"version": "2.4.0",
|
||||
"author": {
|
||||
"name": "XuChang"
|
||||
},
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
"mocha": "^8.2.1",
|
||||
"ts-node": "~10.9.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.7.4"
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@datasert/cronjs-matcher": "^1.2.0",
|
||||
|
|
|
|||
|
|
@ -1 +1,30 @@
|
|||
import { CascadeRelationItem, RelationHierarchy } from "../types/Entity";
|
||||
|
||||
export type GenericRelation = 'owner';
|
||||
|
||||
export function convertHierarchyToAuth<R extends string>(hierarchy: RelationHierarchy<R>): {
|
||||
[K in R]?: CascadeRelationItem;
|
||||
} {
|
||||
const reverseHierarchy: RelationHierarchy<R> = {};
|
||||
for (const r in hierarchy) {
|
||||
for (const r2 of hierarchy[r]!) {
|
||||
if (reverseHierarchy[r2]) {
|
||||
reverseHierarchy[r2]?.push(r);
|
||||
}
|
||||
else {
|
||||
reverseHierarchy[r2] = [r];
|
||||
}
|
||||
}
|
||||
}
|
||||
const result: {
|
||||
[K in R]?: CascadeRelationItem;
|
||||
} = {};
|
||||
for (const r in reverseHierarchy) {
|
||||
result[r] = {
|
||||
cascadePath: '',
|
||||
relations: reverseHierarchy[r],
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
import { EntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
import { createRelationHierarchyCheckers } from '../store/checker';
|
||||
import { createAuthCheckers } from '../store/checker';
|
||||
import { createModiRelatedCheckers } from '../store/modi';
|
||||
import { SyncContext } from '../store/SyncRowStore';
|
||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types';
|
||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefDict } from '../types';
|
||||
|
||||
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>){
|
||||
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){
|
||||
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
||||
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
|
||||
checkers.push(...createRelationHierarchyCheckers<ED, Cxt>(schema));
|
||||
if (authDict) {
|
||||
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
|
||||
}
|
||||
return checkers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ const Schema: Record<string, {
|
|||
states: string[];
|
||||
sourceFile: ts.SourceFile;
|
||||
locale: ts.ObjectLiteralExpression;
|
||||
relationHierarchy?: ts.ObjectLiteralExpression;
|
||||
// relationHierarchy?: ts.ObjectLiteralExpression;
|
||||
// reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
|
||||
toModi: boolean;
|
||||
actionType: string;
|
||||
static: boolean;
|
||||
|
|
@ -497,7 +498,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
const localEnumStringTypes: string[] = [];
|
||||
const additionalImports: ts.ImportDeclaration[] = [];
|
||||
let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
ts.forEachChild(sourceFile!, (node) => {
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
const entityImported = getEntityImported(node);
|
||||
|
|
@ -1123,15 +1125,22 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
_static = true; // static如果有值只能为true
|
||||
}
|
||||
}
|
||||
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
|
||||
/* else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
|
||||
// RelationHierary
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
relationHierarchy = initializer;
|
||||
}
|
||||
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ReverseCascadeRelationHierarchy') {
|
||||
// ReverseCascadeRelationHierarchy
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在ReverseCascadeRelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
reverseCascadeRelationHierarchy = initializer;
|
||||
} */
|
||||
else {
|
||||
throw new Error(`${moduleName}:不能理解的定义内容${declaration.name.getText()}`);
|
||||
throw new Error(`${moduleName}:不能理解的定义内容${(declaration.name as ts.Identifier).text}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -1173,15 +1182,25 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
locale: localeDef,
|
||||
});
|
||||
}
|
||||
if (hasRelationDef) {
|
||||
assert(relationHierarchy, `${filename}中缺少了relationHierarchy定义`);
|
||||
assign(schema, {
|
||||
relationHierarchy,
|
||||
});
|
||||
/* if (hasRelationDef) {
|
||||
if(!relationHierarchy && !reverseCascadeRelationHierarchy){
|
||||
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义,请注意自主编写权限分配的checker`);
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
assign(schema, {
|
||||
relationHierarchy,
|
||||
});
|
||||
}
|
||||
if (reverseCascadeRelationHierarchy) {
|
||||
assign(schema, {
|
||||
reverseCascadeRelationHierarchy,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
|
||||
}
|
||||
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
|
||||
} */
|
||||
|
||||
assign(Schema, {
|
||||
[moduleName]: schema,
|
||||
|
|
@ -2864,10 +2883,6 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
|
|||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("DeduceAggregation"),
|
||||
[
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("Schema"),
|
||||
undefined
|
||||
),
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("Projection"),
|
||||
undefined
|
||||
|
|
@ -3078,6 +3093,13 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
|
|||
const reverseOneNodes: ts.TypeNode[] = [];
|
||||
if (ReversePointerEntities[entity]) {
|
||||
if (ReversePointerRelations[entity]) {
|
||||
const { schemaAttrs } = Schema[entity];
|
||||
const { questionToken } = schemaAttrs.find(
|
||||
ele => {
|
||||
const { name } = ele;
|
||||
return (<ts.Identifier>name).text === 'entity'
|
||||
}
|
||||
)!;
|
||||
for (const one of ReversePointerRelations[entity]) {
|
||||
const cascadeCreateNode = factory.createTypeLiteralNode(
|
||||
[
|
||||
|
|
@ -3136,14 +3158,14 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
|
|||
factory.createPropertySignature(
|
||||
undefined,
|
||||
factory.createIdentifier('entity'),
|
||||
undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc
|
||||
questionToken,
|
||||
factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`)
|
||||
)
|
||||
),
|
||||
factory.createPropertySignature(
|
||||
undefined,
|
||||
factory.createIdentifier('entityId'),
|
||||
undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc
|
||||
questionToken,
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("String"),
|
||||
[factory.createLiteralTypeNode(factory.createNumericLiteral("64"))]
|
||||
|
|
@ -4292,10 +4314,6 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
|
|||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("RemoveOperation"),
|
||||
undefined
|
||||
),
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("SelectOperation"),
|
||||
undefined
|
||||
)
|
||||
])
|
||||
)
|
||||
|
|
@ -4536,23 +4554,6 @@ const initialStatements = () => [
|
|||
function outputSubQuery(outputDir: string, printer: ts.Printer) {
|
||||
const statements: ts.Statement[] = [];
|
||||
if (process.env.COMPLING_AS_LIB) {
|
||||
statements.push(
|
||||
factory.createImportDeclaration(
|
||||
undefined,
|
||||
undefined,
|
||||
factory.createImportClause(
|
||||
false,
|
||||
undefined,
|
||||
factory.createNamedImports([factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("Selection")
|
||||
)])
|
||||
),
|
||||
factory.createStringLiteral(TYPE_PATH_IN_OAK_DOMAIN(1)),
|
||||
undefined
|
||||
)
|
||||
);
|
||||
}
|
||||
for (const entity in Schema) {
|
||||
// import * as User from '../User/Schema';
|
||||
|
|
@ -4903,7 +4904,8 @@ function outputSchema(outputDir: string, printer: ts.Printer) {
|
|||
constructSorter(statements, entity);
|
||||
constructActions(statements, entity);
|
||||
constructQuery(statements, entity);
|
||||
constructFullAttrs(statements, entity);
|
||||
// 现在FullAttrs和NativeAttrs似乎没什么用,还会引起递归
|
||||
// constructFullAttrs(statements, entity);
|
||||
|
||||
const makeActionArguments: ts.TypeNode[] = [];
|
||||
if (ActionAsts[entity]) {
|
||||
|
|
@ -5042,6 +5044,19 @@ function outputSchema(outputDir: string, printer: ts.Printer) {
|
|||
)
|
||||
);
|
||||
}
|
||||
if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef as ts.Node)) {
|
||||
EntityDefAttrs.push(
|
||||
factory.createPropertySignature(
|
||||
undefined,
|
||||
factory.createIdentifier("Relation"),
|
||||
undefined,
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier('Relation'),
|
||||
undefined
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
statements.push(
|
||||
factory.createTypeAliasDeclaration(
|
||||
undefined,
|
||||
|
|
@ -5638,7 +5653,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
|
||||
for (const entity in Schema) {
|
||||
const indexExpressions: ts.Expression[] = [];
|
||||
const { sourceFile, inModi, indexes, toModi, actionType, static: _static, relationHierarchy } = Schema[entity];
|
||||
const { sourceFile, inModi, indexes, toModi, actionType, static: _static, hasRelationDef } = Schema[entity];
|
||||
const fromSchemaSpecifiers = [
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
|
|
@ -5646,7 +5661,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
factory.createIdentifier("OpSchema")
|
||||
)
|
||||
];
|
||||
if (relationHierarchy) {
|
||||
/* if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
fromSchemaSpecifiers.push(
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
|
|
@ -5654,7 +5669,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
factory.createIdentifier("Relation")
|
||||
)
|
||||
);
|
||||
}
|
||||
} */
|
||||
const statements: ts.Statement[] = [
|
||||
factory.createImportDeclaration(
|
||||
undefined,
|
||||
|
|
@ -5855,7 +5870,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
)
|
||||
);
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
/* if (relationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relationHierarchy"),
|
||||
|
|
@ -5863,20 +5878,55 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
)
|
||||
);
|
||||
}
|
||||
if (reverseCascadeRelationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("reverseCascadeRelationHierarchy"),
|
||||
reverseCascadeRelationHierarchy,
|
||||
)
|
||||
);
|
||||
} */
|
||||
if (hasRelationDef) {
|
||||
const { type } = hasRelationDef;
|
||||
if (ts.isUnionTypeNode(type)) {
|
||||
const { types } = type;
|
||||
const relationTexts = types.map(
|
||||
ele => {
|
||||
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal));
|
||||
return ele.literal.text;
|
||||
}
|
||||
)
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relation"),
|
||||
factory.createArrayLiteralExpression(relationTexts.map(
|
||||
ele => factory.createStringLiteral(ele)
|
||||
)),
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
assert(ts.isLiteralTypeNode(type));
|
||||
assert(ts.isStringLiteral(type.literal));
|
||||
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relation"),
|
||||
factory.createArrayLiteralExpression(
|
||||
[
|
||||
type.literal
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
const sdTypeArguments = [
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("OpSchema"),
|
||||
undefined
|
||||
)
|
||||
];
|
||||
if (relationHierarchy) {
|
||||
sdTypeArguments.push(
|
||||
factory.createTypeReferenceNode(
|
||||
factory.createIdentifier("Relation"),
|
||||
undefined
|
||||
)
|
||||
)
|
||||
}
|
||||
statements.push(
|
||||
factory.createVariableStatement(
|
||||
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import assert from "assert";
|
||||
import {
|
||||
EntityDict,
|
||||
OperateOption, SelectOption, OperationResult, DeduceFilter, CreateAtAttribute, UpdateAtAttribute, AggregationResult, DeleteAtAttribute
|
||||
OperateOption, SelectOption, OperationResult, CreateAtAttribute, UpdateAtAttribute, AggregationResult, DeleteAtAttribute
|
||||
} from "../types/Entity";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { RowStore } from '../types/RowStore';
|
||||
|
|
@ -525,7 +525,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
operation: ED[T2]['Operation'],
|
||||
context: Cxt,
|
||||
option: OP) => R,
|
||||
filter?: DeduceFilter<ED[T]['Schema']>
|
||||
filter?: ED[T]['Update']['filter']
|
||||
) {
|
||||
const modiAttr = this.getSchema()[entity].toModi;
|
||||
const option2 = Object.assign({}, option);
|
||||
|
|
@ -830,7 +830,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
}
|
||||
|
||||
// 对更新的数据,去掉所有的undefined属性
|
||||
protected preProcessDataUpdated<T extends keyof ED>(data: ED[T]['Update']['data']) {
|
||||
protected preProcessDataUpdated(data: Record<string, any>) {
|
||||
const undefinedKeys = Object.keys(data).filter(
|
||||
ele => data[ele] === undefined
|
||||
);
|
||||
|
|
@ -852,7 +852,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
* @param option
|
||||
*/
|
||||
private async doUpdateSingleRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(entity: T,
|
||||
operation: ED[T]['Operation'],
|
||||
operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'],
|
||||
context: Cxt,
|
||||
option: OP
|
||||
) {
|
||||
|
|
@ -1182,7 +1182,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
id: {
|
||||
$in: ids,
|
||||
}
|
||||
} as DeduceFilter<ED[T]['Schema']>,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1202,7 +1202,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
id: {
|
||||
$in: ids,
|
||||
}
|
||||
} as DeduceFilter<ED[T]['Schema']>,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import assert from 'assert';
|
||||
import { pull, unset } from "../utils/lodash";
|
||||
import { addFilterSegment, checkFilterRepel } from "../store/filter";
|
||||
import { DeduceCreateOperation, EntityDict, OperateOption, SelectOption, TriggerDataAttribute, TriggerTimestampAttribute } from "../types/Entity";
|
||||
import { EntityDict, OperateOption, SelectOption, TriggerDataAttribute, TriggerTimestampAttribute } from "../types/Entity";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { Logger } from "../types/Logger";
|
||||
import { Checker, CheckerType } from '../types/Auth';
|
||||
import { Checker, CheckerType, LogicalChecker, RelationChecker } from '../types/Auth';
|
||||
import { Trigger, CreateTriggerCrossTxn, CreateTrigger, CreateTriggerInTxn, SelectTriggerAfter, UpdateTrigger } from "../types/Trigger";
|
||||
import { AsyncContext } from './AsyncRowStore';
|
||||
import { SyncContext } from './SyncRowStore';
|
||||
|
|
@ -49,15 +49,15 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
registerChecker<T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): void {
|
||||
const { entity, action, type, conditionalFilter } = checker;
|
||||
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
|
||||
const fn = translateCheckerInAsyncContext(checker);
|
||||
const { fn, when } = translateCheckerInAsyncContext(checker);
|
||||
const trigger = {
|
||||
checkerType: type,
|
||||
name: triggerName,
|
||||
priority: checker.priority || 2, // checker的默认优先级稍高一点点
|
||||
priority: checker.priority || 20, // checker的默认优先级稍高
|
||||
entity,
|
||||
action: action as 'update',
|
||||
fn,
|
||||
when: 'before',
|
||||
when,
|
||||
filter: conditionalFilter,
|
||||
} as UpdateTrigger<ED, T, Cxt>;
|
||||
this.registerTrigger(trigger);
|
||||
|
|
@ -77,7 +77,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
throw new Error(`不可有同名的触发器「${trigger.name}」`);
|
||||
}
|
||||
if (typeof trigger.priority !== 'number') {
|
||||
trigger.priority = 1; // 默认最低
|
||||
trigger.priority = 10; // 默认值
|
||||
}
|
||||
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
|
||||
assert(typeof trigger.action === 'string' && trigger.action !== 'create'
|
||||
|
|
@ -229,8 +229,9 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
// trigger只对满足条件的前项进行判断,如果确定不满足可以pass
|
||||
assert(operation.action !== 'create');
|
||||
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
|
||||
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const filterRepelled = checkFilterRepel(entity, context, filterr, operation.filter) as boolean
|
||||
const filterr = typeof filter === 'function' ? filter(operation as ED[T]['Update'], context, option) : filter;
|
||||
assert(!(filterr instanceof Promise));
|
||||
const filterRepelled = checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as boolean
|
||||
if (filterRepelled) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -252,8 +253,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
|
||||
assert(operation.action !== 'create');
|
||||
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
|
||||
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const filterRepelled = await (checkFilterRepel(entity, context, filterr, operation.filter) as Promise<boolean>);
|
||||
const filterr = typeof filter === 'function' ? await filter(operation as ED[T]['Update'], context, option) : filter;
|
||||
const filterRepelled = await (checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as Promise<boolean>);
|
||||
if (filterRepelled) {
|
||||
return execPreTrigger(idx + 1);
|
||||
}
|
||||
|
|
@ -272,8 +273,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
|
||||
assert(operation.action !== 'create');
|
||||
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
|
||||
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const filterRepelled = await (checkFilterRepel(entity, context, filterr, operation.filter) as Promise<boolean>);
|
||||
const filterr = typeof filter === 'function' ? await filter(operation as ED[T]['Update'], context, option) : filter;
|
||||
const filterRepelled = await (checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as Promise<boolean>);
|
||||
if (filterRepelled) {
|
||||
return execCommitTrigger(idx + 1);
|
||||
}
|
||||
|
|
@ -294,7 +295,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
return async () => {
|
||||
await context.begin();
|
||||
const number = await (trigger as CreateTrigger<ED, T, AsyncContext<ED>>).fn({
|
||||
operation: operation as DeduceCreateOperation<ED[T]['Schema']>,
|
||||
operation: operation as ED[T]['Create'],
|
||||
}, context, option);
|
||||
if ((trigger as CreateTriggerCrossTxn<ED, T, Cxt>).strict === 'makeSure') {
|
||||
// 如果是必须完成的trigger,在完成成功后要把trigger相关的属性置null;
|
||||
|
|
@ -354,10 +355,10 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
|
|||
);
|
||||
if (triggers) {
|
||||
const postTriggers = triggers.filter(
|
||||
ele => ele.when === 'after' && (!(ele as CreateTrigger<ED, T, Cxt>).check || (ele as CreateTrigger<ED, T, Cxt>).check!(operation as DeduceCreateOperation<ED[T]['Schema']>))
|
||||
ele => ele.when === 'after' && (!(ele as CreateTrigger<ED, T, Cxt>).check || (ele as CreateTrigger<ED, T, Cxt>).check!(operation as ED[T]['Create']))
|
||||
);
|
||||
const commitTriggers = (<Array<CreateTrigger<ED, T, Cxt>>>triggers).filter(
|
||||
ele => ele.when === 'commit' && (!ele.check || ele.check(operation as DeduceCreateOperation<ED[T]['Schema']>))
|
||||
ele => ele.when === 'commit' && (!ele.check || ele.check(operation as ED[T]['Create']))
|
||||
);
|
||||
|
||||
if (context instanceof SyncContext) {
|
||||
|
|
|
|||
|
|
@ -1,38 +1,51 @@
|
|||
import assert from 'assert';
|
||||
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
|
||||
import { OakDataException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
|
||||
import { Checker, CreateTriggerInTxn, EntityDict, ExpressionRelationChecker, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn } from "../types";
|
||||
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
|
||||
import {
|
||||
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
|
||||
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy
|
||||
} from "../types";
|
||||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from "./AsyncRowStore";
|
||||
import { getFullProjection } from './actionDef';
|
||||
import { SyncContext } from './SyncRowStore';
|
||||
import { firstLetterUpperCase } from '../utils/string';
|
||||
import { uniq } from '../utils/lodash';
|
||||
import { judgeRelation } from './relation';
|
||||
|
||||
export function translateCheckerInAsyncContext<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
Cxt extends AsyncContext<ED>
|
||||
>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn'] {
|
||||
const { entity, type } = checker;
|
||||
>(checker: Checker<ED, keyof ED, Cxt>): {
|
||||
fn: Trigger<ED, keyof ED, Cxt>['fn'];
|
||||
when: 'before' | 'after';
|
||||
} {
|
||||
const { entity, type, action } = checker;
|
||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
const { checker: checkerFn } = checker;
|
||||
return (async ({ operation }, context) => {
|
||||
const fn = (async ({ operation }, context) => {
|
||||
const { data } = operation;
|
||||
checkerFn(data, context);
|
||||
await checkerFn(data, context);
|
||||
return 0;
|
||||
}) as CreateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
case 'row': {
|
||||
const { filter, errMsg, inconsistentRows } = checker;
|
||||
return (async ({ operation }, context, option) => {
|
||||
const fn = (async ({ operation }, context, option) => {
|
||||
const { filter: operationFilter, action } = operation;
|
||||
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const filter2 = typeof filter === 'function' ? await (filter as Function)(operation, context, option) : filter;
|
||||
if (['select', 'count', 'stat'].includes(action)) {
|
||||
operation.filter = addFilterSegment(operationFilter || {}, filter2);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (await checkFilterContains(entity, context, filter2, operationFilter || {})) {
|
||||
if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, operationFilter || {}, true)) {
|
||||
return 0;
|
||||
}
|
||||
if (inconsistentRows) {
|
||||
|
|
@ -81,47 +94,61 @@ export function translateCheckerInAsyncContext<
|
|||
}
|
||||
}
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
case 'relation': {
|
||||
const { relationFilter } = checker;
|
||||
return (async ({ operation }, context, option) => {
|
||||
const { relationFilter, errMsg } = checker;
|
||||
const fn = (async ({ operation }, context, option) => {
|
||||
if (context.isRoot()) {
|
||||
return 0;
|
||||
}
|
||||
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
||||
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
||||
operation.filter = combineFilters([operation.filter, relationFilter(operation, context, option)]);
|
||||
return 0;
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
}
|
||||
case 'expression':
|
||||
case 'expressionRelation': {
|
||||
const { expression, errMsg } = checker;
|
||||
return (async ({ operation }, context, option) => {
|
||||
if (context.isRoot() && type === 'expressionRelation') {
|
||||
return 0;
|
||||
}
|
||||
const exprResult = expression(operation, context, option);
|
||||
if (typeof exprResult === 'string') {
|
||||
throw new OakUserUnpermittedException(exprResult || errMsg);
|
||||
}
|
||||
else if (exprResult === undefined) {
|
||||
return 0;
|
||||
if (operation.action === 'create') {
|
||||
const filter2 = await relationFilter(operation, context, option);
|
||||
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
const filter = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(
|
||||
ele => ele.id,
|
||||
),
|
||||
},
|
||||
} : {
|
||||
id: data.id,
|
||||
};
|
||||
if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, filter, true)) {
|
||||
return 0;
|
||||
}
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
}
|
||||
else {
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
|
||||
const [result] = await context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true }));
|
||||
if (!result) {
|
||||
// 条件判定为假,抛异常
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
}
|
||||
operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
|
||||
}
|
||||
return 0;
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
case 'logical':
|
||||
case 'logicalRelation': {
|
||||
const { checker: checkerFn } = checker;
|
||||
const fn = (async ({ operation }, context, option) => {
|
||||
if (context.isRoot() && type === 'logicalRelation') {
|
||||
return 0;
|
||||
}
|
||||
await checkerFn(operation, context, option);
|
||||
return 0;
|
||||
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
|
|
@ -133,74 +160,85 @@ export function translateCheckerInSyncContext<
|
|||
ED extends EntityDict & BaseEntityDict,
|
||||
T extends keyof ED,
|
||||
Cxt extends SyncContext<ED>
|
||||
>(checker: Checker<ED, T, Cxt>): (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void {
|
||||
const { entity, type } = checker;
|
||||
>(checker: Checker<ED, T, Cxt>): {
|
||||
fn: (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
|
||||
when: 'before' | 'after';
|
||||
} {
|
||||
const { entity, type, action } = checker;
|
||||
const when = ((action === 'create' || action instanceof Array && action.includes('create')) && ['relation'].includes(type)) ? 'after' : 'before'
|
||||
switch (type) {
|
||||
case 'data': {
|
||||
const { checker: checkerFn } = checker;
|
||||
return (operation, context) => checkerFn(operation.data, context);
|
||||
const fn = (operation: ED[T]['Operation'], context: Cxt) => checkerFn(operation.data, context);
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
}
|
||||
}
|
||||
case 'row': {
|
||||
const { filter, errMsg } = checker;
|
||||
return (operation, context, option) => {
|
||||
const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
const { filter: operationFilter, action } = operation;
|
||||
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const filter2 = typeof filter === 'function' ? (filter as Function)(operation, context, option) : filter;
|
||||
assert(operationFilter);
|
||||
if (['select', 'count', 'stat'].includes(action)) {
|
||||
operation.filter = addFilterSegment(operationFilter, filter2);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, operationFilter)) {
|
||||
assert(!(filter2 instanceof Promise));
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, operationFilter, true)) {
|
||||
return;
|
||||
}
|
||||
throw new OakRowInconsistencyException(undefined, errMsg);
|
||||
}
|
||||
};
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
case 'relation': {
|
||||
const { relationFilter: filter, errMsg } = checker;
|
||||
return (operation, context, option) => {
|
||||
const { relationFilter, errMsg } = checker;
|
||||
const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
if (context.isRoot()) {
|
||||
return;
|
||||
}
|
||||
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||
const { filter: operationFilter } = operation;
|
||||
assert(operationFilter);
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, operationFilter)) {
|
||||
const filter2 = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
|
||||
const { filter, action } = operation;
|
||||
let filter3 = filter;
|
||||
if (action === 'create') {
|
||||
const { data } = operation as ED[T]['Create'];
|
||||
filter3 = data instanceof Array ? {
|
||||
id: {
|
||||
$in: data.map(ele => ele.id),
|
||||
},
|
||||
} : { id: data.id };
|
||||
}
|
||||
assert(filter3);
|
||||
assert(!(filter2 instanceof Promise));
|
||||
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, filter3, true)) {
|
||||
return;
|
||||
}
|
||||
throw new OakUserUnpermittedException(errMsg);
|
||||
};
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
case 'expression':
|
||||
case 'expressionRelation': {
|
||||
const { expression, errMsg } = checker;
|
||||
return (operation, context, option) => {
|
||||
if (context.isRoot() && type === 'expressionRelation') {
|
||||
return;
|
||||
}
|
||||
const exprResult = expression(operation, context, option);
|
||||
if (typeof exprResult === 'string') {
|
||||
throw new OakUserUnpermittedException(exprResult || errMsg);
|
||||
}
|
||||
else if (exprResult === undefined) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
|
||||
const [result] = context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true })) as any[];
|
||||
if (!result.$expr) {
|
||||
// 条件判定为假,抛异常
|
||||
throw new OakRowInconsistencyException(undefined, errMsg);
|
||||
}
|
||||
case 'logical':
|
||||
case 'logicalRelation': {
|
||||
const { checker: checkerFn } = checker;
|
||||
const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
if (context.isRoot() && type === 'logicalRelation') {
|
||||
return;
|
||||
}
|
||||
checkerFn(operation, context, option);
|
||||
};
|
||||
return {
|
||||
fn,
|
||||
when,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
|
|
@ -209,141 +247,232 @@ export function translateCheckerInSyncContext<
|
|||
}
|
||||
}
|
||||
|
||||
function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
lch: CascadeRelationItem,
|
||||
entity2: keyof ED): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
||||
const { cascadePath, relations } = lch;
|
||||
const paths = cascadePath.split('.');
|
||||
|
||||
export function createRelationHierarchyCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>) {
|
||||
const translateRelationFilter = <T extends keyof ED>(entity: T): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||
// 有两种情况,此entity和user有Relation定义,或是此entity上有userId
|
||||
if (schema[entity].relation) {
|
||||
const relationEntityName = `user${firstLetterUpperCase(entity as string)}`;
|
||||
return (userId) => {
|
||||
const filter = relations ? {
|
||||
userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
} : {
|
||||
userId,
|
||||
};
|
||||
return {
|
||||
id: {
|
||||
$in: {
|
||||
entity: relationEntityName,
|
||||
data: {
|
||||
[`${entity as string}Id`]: 1,
|
||||
},
|
||||
filter,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const { attributes } = schema[entity];
|
||||
assert(attributes.hasOwnProperty('userId') && attributes.userId.type === 'ref' && attributes.userId.ref === 'user', `在${entity as string}上既找不到userId,也没有relation定义`);
|
||||
return (userId) => ({
|
||||
userId,
|
||||
});
|
||||
};
|
||||
|
||||
const translateFilterMakerIter = <T extends keyof ED>(entity: T, iter: number): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||
const relation = judgeRelation(schema, entity, paths[iter]);
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
const filterMaker = translateRelationFilter(paths[iter]);
|
||||
return (userId) => {
|
||||
const filter = filterMaker(userId);
|
||||
if (filter!.$in) {
|
||||
return {
|
||||
entity: paths[iter],
|
||||
entityId: filter,
|
||||
};
|
||||
}
|
||||
return {
|
||||
[paths[iter]]: filter,
|
||||
};
|
||||
}
|
||||
}
|
||||
assert(typeof relation === 'string');
|
||||
const filterMaker = translateRelationFilter(relation);
|
||||
return (userId) => {
|
||||
const filter = filterMaker(userId);
|
||||
|
||||
if (filter!.$in) {
|
||||
return {
|
||||
[`${paths[iter]}Id`]: filter
|
||||
};
|
||||
}
|
||||
return {
|
||||
[paths[iter]]: filter,
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
const subFilterMaker = translateFilterMakerIter(paths[iter], iter + 1);
|
||||
if (iter === 0) {
|
||||
return (userId) => {
|
||||
const subFilter = subFilterMaker(userId);
|
||||
return {
|
||||
[paths[iter]]: subFilter,
|
||||
};
|
||||
};
|
||||
}
|
||||
return (userId) => ({
|
||||
[paths[iter]]: subFilterMaker(userId),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const filter = cascadePath ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
}
|
||||
|
||||
function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
||||
entity: keyof ED
|
||||
): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
||||
if (relationItem instanceof Array) {
|
||||
const maker = relationItem.map(
|
||||
ele => {
|
||||
if (ele instanceof Array) {
|
||||
return ele.map(
|
||||
ele2 => translateCascadeRelationFilterMaker(schema, ele2, entity)
|
||||
);
|
||||
}
|
||||
return [translateCascadeRelationFilterMaker(schema, ele, entity)];
|
||||
}
|
||||
);
|
||||
return (userId) => ({
|
||||
$or: maker.map(
|
||||
ele => ({
|
||||
$and: ele.map(
|
||||
ele2 => ele2(userId)!
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
const filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||
return (userId) => filterMaker(userId);
|
||||
}
|
||||
|
||||
|
||||
export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
|
||||
schema: StorageSchema<ED>,
|
||||
authDict: AuthDefDict<ED>) {
|
||||
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
||||
|
||||
for (const entity in schema) {
|
||||
const { relationHierarchy } = schema[entity];
|
||||
if (relationHierarchy) {
|
||||
// 先build反向hierarchy的map
|
||||
const reverseHierarchy = {} as Record<string, string[]>;
|
||||
for (const r in relationHierarchy) {
|
||||
for (const r2 of relationHierarchy[r]!) {
|
||||
if (!reverseHierarchy[r2]) {
|
||||
reverseHierarchy[r2] = [r];
|
||||
}
|
||||
else {
|
||||
reverseHierarchy[r2].push(r);
|
||||
}
|
||||
if (authDict[entity]) {
|
||||
const { relationAuth, actionAuth } = authDict[entity]!;
|
||||
if (relationAuth) {
|
||||
const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
|
||||
for (const r in relationAuth) {
|
||||
Object.assign(raFilterMakerDict, {
|
||||
[r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 对userEntity对象的授权和回收建立checker
|
||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||
const entityIdAttr = `${entity}Id`;
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => {
|
||||
const { data } = operation as ED[keyof ED]['Operation'];
|
||||
const { relation, [entityIdAttr]: entityId } = data as Record<string, string>;
|
||||
const legalRelations = reverseHierarchy[relation];
|
||||
if (!legalRelations) {
|
||||
return undefined;
|
||||
}
|
||||
if (legalRelations.length === 0) {
|
||||
return '这是不应该跑出来的情况,请杀程序员祭天';
|
||||
}
|
||||
const userId = context.getCurrentUserId();
|
||||
return {
|
||||
entity: userEntityName as T2,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
[entityIdAttr]: entityId,
|
||||
relation: {
|
||||
$in: legalRelations,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
for (const r in reverseHierarchy) {
|
||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||
const entityIdAttr = `${entity}Id`;
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'remove',
|
||||
type: 'expressionRelation',
|
||||
conditionalFilter: {
|
||||
relation: r,
|
||||
} as ED[keyof ED]['Update']['filter'],
|
||||
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => {
|
||||
action: 'create',
|
||||
type: 'relation',
|
||||
relationFilter: (operation, context) => {
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
assert(!(data instanceof Array));
|
||||
const { relation, [entityIdAttr]: entityId } = data;
|
||||
const userId = context.getCurrentUserId();
|
||||
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||
const legalRelations = reverseHierarchy[r];
|
||||
if (legalRelations.length === 0) {
|
||||
return '这是不应该跑出来的情况,请杀程序员祭天';
|
||||
if (!raFilterMakerDict[relation]) {
|
||||
return;
|
||||
}
|
||||
const filter = raFilterMakerDict[relation]!(userId!);
|
||||
return {
|
||||
entity: userEntityName as T2,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
[entityIdAttr]: {
|
||||
$in: {
|
||||
entity: userEntityName,
|
||||
data: {
|
||||
[entityIdAttr]: 1,
|
||||
},
|
||||
filter,
|
||||
}
|
||||
},
|
||||
relation: {
|
||||
$in: legalRelations,
|
||||
}
|
||||
},
|
||||
}
|
||||
[entity]: filter,
|
||||
};
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'remove' as ED[keyof ED]['Action'],
|
||||
type: 'relation',
|
||||
relationFilter: (operation: any, context: Cxt) => {
|
||||
const userId = context.getCurrentUserId();
|
||||
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]) => {
|
||||
const relations = uniq(rows.map(ele => ele.relation));
|
||||
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
|
||||
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
|
||||
// const entityId = entityIds[0]!;
|
||||
|
||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||
return {
|
||||
$and: relations.map(
|
||||
(relation) => raFilterMakerDict[relation!]
|
||||
).filter(
|
||||
ele => !!ele
|
||||
).map(
|
||||
ele => ({
|
||||
[entity]: ele(userId!),
|
||||
})
|
||||
)
|
||||
} as ED[keyof ED]['Selection']['filter'];
|
||||
};
|
||||
|
||||
const toBeRemoved = context.select(userEntityName, {
|
||||
data: {
|
||||
id: 1,
|
||||
relation: 1,
|
||||
[entityIdAttr]: 1,
|
||||
},
|
||||
filter,
|
||||
}, { dontCollect: true });
|
||||
if (toBeRemoved instanceof Promise) {
|
||||
return toBeRemoved.then(
|
||||
(rows) => makeFilterFromRows(rows)
|
||||
);
|
||||
}
|
||||
return makeFilterFromRows(toBeRemoved);
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
// 转让权限现在用update动作,只允许update userId给其它人
|
||||
// todo 等实现的时候再写
|
||||
}
|
||||
|
||||
/* // 一个人不能授权给自己,也不能删除自己的授权
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'create' as ED[keyof ED]['Action'],
|
||||
type: 'data',
|
||||
checker: (data, context) => {
|
||||
assert(!(data instanceof Array));
|
||||
const { userId } = data as ED[keyof ED]['CreateSingle']['data'];
|
||||
const userId2 = context.getCurrentUserId(true);
|
||||
if (userId === userId2) {
|
||||
throw new OakDataException('不允许授权给自己');
|
||||
}
|
||||
if (actionAuth) {
|
||||
for (const a in actionAuth) {
|
||||
const filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a as ED[keyof ED]['Action']]!, entity);
|
||||
checkers.push({
|
||||
entity,
|
||||
action: a as ED[keyof ED]['Action'],
|
||||
type: 'relation',
|
||||
relationFilter: (operation, context) => {
|
||||
// const { filter } = operation;
|
||||
const filter = filterMaker(context.getCurrentUserId()!);
|
||||
return filter;
|
||||
},
|
||||
errMsg: '定义的actionAuth中检查出来越权操作',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'remove' as ED[keyof ED]['Action'],
|
||||
type: 'row',
|
||||
filter: (operation, context) => {
|
||||
const userId = context.getCurrentUserId(true);
|
||||
if (userId) {
|
||||
return {
|
||||
userId: {
|
||||
$ne: userId,
|
||||
},
|
||||
};
|
||||
}
|
||||
console.warn(`没有当前用户但在删除权限,请检查。对象是${entity}`);
|
||||
return {};
|
||||
},
|
||||
errMsg: '不允许回收自己的授权',
|
||||
}); */
|
||||
|
||||
// 转让权限现在用update动作,只允许update userId给其它人
|
||||
// todo 等实现的时候再写
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -732,14 +732,14 @@ export function getRelevantIds<ED extends EntityDict, T extends keyof ED>(filter
|
|||
|
||||
if (filter?.$and) {
|
||||
const idss = filter.$and.map(
|
||||
ele => getRelevantIds(ele)
|
||||
(ele: ED[T]['Selection']['filter']) => getRelevantIds(ele)
|
||||
);
|
||||
idsAnd = intersection(...idss);
|
||||
}
|
||||
|
||||
if (filter?.$or) {
|
||||
const idss = filter.$or.map(
|
||||
ele => getRelevantIds(ele)
|
||||
(ele: ED[T]['Selection']['filter']) => getRelevantIds(ele)
|
||||
);
|
||||
idsOr = union(...idss);
|
||||
}
|
||||
|
|
@ -879,11 +879,21 @@ export function makeTreeDescendantFilter<ED extends EntityDict, T extends keyof
|
|||
return currentLevelInFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查filter是否包含contained(filter查询的数据一定满足contained)
|
||||
* @param entity
|
||||
* @param context
|
||||
* @param contained
|
||||
* @param filter
|
||||
* @param dataCompare
|
||||
* @returns
|
||||
*/
|
||||
export function checkFilterContains<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(
|
||||
entity: T,
|
||||
context: Cxt,
|
||||
contained: ED[T]['Selection']['filter'],
|
||||
filter?: ED[T]['Selection']['filter']): boolean | Promise<boolean> {
|
||||
filter?: ED[T]['Selection']['filter'],
|
||||
dataCompare?: true): boolean | Promise<boolean> {
|
||||
if (!filter) {
|
||||
throw new OakRowInconsistencyException();
|
||||
}
|
||||
|
|
@ -892,29 +902,34 @@ export function checkFilterContains<ED extends EntityDict, T extends keyof ED, C
|
|||
if (contains(entity, schema, filter, contained)) {
|
||||
return true;
|
||||
}
|
||||
// 再判断加上了conditionalFilter后取得的行数是否缩减
|
||||
const filter2 = combineFilters([filter, {
|
||||
$not: contained,
|
||||
}]);
|
||||
const count = context.count(entity, {
|
||||
filter: filter2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(
|
||||
(count2) => count2 === 0
|
||||
);
|
||||
if (dataCompare) {
|
||||
// 再判断加上了conditionalFilter后取得的行数是否缩减
|
||||
const filter2 = combineFilters([filter, {
|
||||
$not: contained,
|
||||
}]);
|
||||
const count = context.count(entity, {
|
||||
filter: filter2,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(
|
||||
(count2) => count2 === 0
|
||||
);
|
||||
}
|
||||
return count === 0;
|
||||
}
|
||||
return count === 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(
|
||||
entity: T,
|
||||
context: Cxt,
|
||||
filter1: ED[T]['Selection']['filter'],
|
||||
filter2: ED[T]['Selection']['filter']): boolean | Promise<boolean> {
|
||||
filter2: ED[T]['Selection']['filter'],
|
||||
dataCompare?: true
|
||||
): boolean | Promise<boolean> {
|
||||
if (!filter2) {
|
||||
throw new OakRowInconsistencyException();
|
||||
}
|
||||
|
|
@ -924,17 +939,20 @@ export function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt
|
|||
return true;
|
||||
}
|
||||
// 再判断两者同时成立时取得的行数是否为0
|
||||
const filter3 = combineFilters([filter2, filter1]);
|
||||
const count = context.count(entity, {
|
||||
filter: filter3,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(
|
||||
(count2) => count2 === 0
|
||||
);
|
||||
if (dataCompare) {
|
||||
const filter3 = combineFilters([filter2, filter1]);
|
||||
const count = context.count(entity, {
|
||||
filter: filter3,
|
||||
}, {
|
||||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
});
|
||||
if (count instanceof Promise) {
|
||||
return count.then(
|
||||
(count2) => count2 === 0
|
||||
);
|
||||
}
|
||||
return count === 0;
|
||||
}
|
||||
return count === 0;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
||||
import { OpSchema as Modi, Filter } from '../base-app-domain/Modi/Schema';
|
||||
import { Checker, Operation, StorageSchema, RowChecker, EntityDict, OakRowLockedException, Context, OperateOption, Trigger, RemoveTrigger, RelationChecker, ExpressionChecker, ExpressionRelationChecker, OakUserUnpermittedException } from '../types';
|
||||
import { Checker, Operation, StorageSchema, RowChecker, EntityDict, OakRowLockedException, Context, OperateOption, Trigger, RemoveTrigger, RelationChecker, LogicalChecker, LogicalRelationChecker, OakUserUnpermittedException } from '../types';
|
||||
import { appendOnlyActions } from '../actions/action';
|
||||
import { difference } from '../utils/lodash';
|
||||
import { AsyncContext } from './AsyncRowStore';
|
||||
|
|
@ -52,14 +52,14 @@ export async function abandonModis<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
action: 'abandon',
|
||||
data: {},
|
||||
filter,
|
||||
sorter: [
|
||||
/* sorter: [
|
||||
{
|
||||
$attr: {
|
||||
$$createAt$$: 1,
|
||||
},
|
||||
$direction: 'asc',
|
||||
}
|
||||
]
|
||||
] */
|
||||
}, Object.assign({}, option, {
|
||||
blockTrigger: false,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function judgeRelation<ED extends {
|
|||
return 2;
|
||||
}
|
||||
else {
|
||||
assert(initinctiveAttributes.includes(attr), `${attr}属性找不到`);
|
||||
assert(initinctiveAttributes.includes(attr), `${entity as string}对象中的${attr}属性找不到`);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'assert';
|
||||
import { getAttrRefInExpression, StorageSchema } from '../types';
|
||||
import { EXPRESSION_PREFIX } from '../types/Demand';
|
||||
import { DeduceFilter, EntityDict } from '../types/Entity';
|
||||
import { EntityDict } from '../types/Entity';
|
||||
import { getRelevantIds } from './filter';
|
||||
import { judgeRelation } from './relation';
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<
|
|||
const toBeAssignNode: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
|
||||
// filter当中所关联到的属性必须在projection中
|
||||
const filterNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
|
||||
const checkFilterNode = (entity2: keyof ED, filterNode: DeduceFilter<ED[keyof ED]['Schema']>, projectionNode: ED[keyof ED]['Selection']['data']) => {
|
||||
const checkFilterNode = (entity2: keyof ED, filterNode: ED[keyof ED]['Selection']['filter'], projectionNode: ED[keyof ED]['Selection']['data']) => {
|
||||
const necessaryAttrs: string[] = ['id'];
|
||||
for (const attr in filterNode) {
|
||||
if (attr === '#id') {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { EntityDict } from "./Entity";
|
||||
import { CascadeRelationItem, EntityDict } from "./Entity";
|
||||
import { GenericAction } from '../actions/action';
|
||||
|
||||
export type Action = string;
|
||||
export type State = string;
|
||||
|
|
@ -15,3 +16,8 @@ export type ActionDictOfEntityDict<E extends EntityDict> = {
|
|||
[A in keyof E[T]['OpSchema']]?: ActionDef<string, string>;
|
||||
};
|
||||
};
|
||||
|
||||
// 即在cascadePath指向的对象上,有relation关系。若relation为空则不限定关系
|
||||
export type CascadeActionAuth<A extends Action = ''> = {
|
||||
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
|
||||
import { RefOrExpression } from "./Expression";
|
||||
|
||||
export type CheckerType = 'relation' | 'row' | 'data' | 'expression' | 'expressionRelation';
|
||||
export type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation';
|
||||
|
||||
/**
|
||||
* conditionalFilter是指该action发生时,operation所操作的行中有满足conditionalFilter的行
|
||||
|
|
@ -14,8 +15,10 @@ export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends A
|
|||
type: 'data';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
|
|
@ -23,54 +26,70 @@ export type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends As
|
|||
type: 'row';
|
||||
entity: T;
|
||||
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']); // 对行的额外检查条件
|
||||
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']>
|
||||
); // 对行的额外检查条件
|
||||
errMsg?: string;
|
||||
inconsistentRows?: { // 因为这里的限制不一定在本row上,如果不传这个exception,则默认返回本row上的exception
|
||||
entity: keyof ED;
|
||||
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']);
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
export type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'relation';
|
||||
entity: T;
|
||||
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
|
||||
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter']; // 生成一个额外的relation相关的filter,加在原先的filter上
|
||||
when?: 'after';
|
||||
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上
|
||||
errMsg: string;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | (
|
||||
(operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>
|
||||
);
|
||||
};
|
||||
|
||||
export type LogicalChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'logical';
|
||||
when?: 'after';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
checker: (
|
||||
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
||||
context: Cxt,
|
||||
option: OperateOption | SelectOption
|
||||
) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
};
|
||||
|
||||
export type ExpressionChecker<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>> = {
|
||||
priority?: number;
|
||||
type: 'expression';
|
||||
type: 'logicalRelation';
|
||||
when?: 'after';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
expression: <T2 extends keyof ED>(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
entity: T2;
|
||||
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
|
||||
filter: ED[T2]['Selection']['filter'];
|
||||
} | undefined | string; // 生成一个带表达式的查询任务,结果为true代表可以过,为false不可以。如果返回undefined直接过,返回string直接挂
|
||||
errMsg: string;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
};
|
||||
|
||||
|
||||
export type ExpressionRelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
|
||||
priority?: number;
|
||||
type: 'expressionRelation';
|
||||
entity: T;
|
||||
action: ED[T]['Action'] | Array<ED[T]['Action']>;
|
||||
expression: <T2 extends keyof ED>(operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||
entity: T2;
|
||||
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
|
||||
filter: ED[T2]['Selection']['filter'];
|
||||
} | undefined | string; // 生成一个带表达式的查询任务,结果为true代表可以过。如果返回undefined直接过,返回string直接挂
|
||||
errMsg: string;
|
||||
checker: (
|
||||
operation: ED[T]['Operation'] | ED[T]['Selection'],
|
||||
context: Cxt,
|
||||
option: OperateOption | SelectOption
|
||||
) => void | Promise<void>;
|
||||
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
};
|
||||
|
||||
|
||||
export 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> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<ED, T, Cxt>;
|
||||
DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | LogicalChecker<ED, T, Cxt> | LogicalRelationChecker<ED, T, Cxt>;
|
||||
|
||||
|
||||
export type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
||||
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||
};
|
||||
|
||||
export type AuthDefDict<ED extends EntityDict> = {
|
||||
[K in keyof ED]?: AuthDef<ED, K>;
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { ClientRequest, IncomingHttpHeaders, IncomingMessage } from "http";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { EntityDict } from "./Entity";
|
||||
|
||||
export interface Endpoint<ED extends EntityDict, BackCxt extends AsyncContext<ED>> {
|
||||
name: string;
|
||||
params?: string[];
|
||||
method: 'get' | 'post' | 'put' | 'delete';
|
||||
fn: (context: BackCxt, params: Record<string, string>, headers: IncomingHttpHeaders,
|
||||
req: IncomingMessage, body?: any) => Promise<any>;
|
||||
};
|
||||
|
|
@ -1,6 +1,3 @@
|
|||
import { GenericAction } from '../actions/action';
|
||||
import { ExpressionKey, ExprOp, FulltextFilter, MakeFilter, NodeId, Q_BooleanValue, Q_NumberValue, Q_StringValue } from './Demand';
|
||||
import { OneOf, OptionalKeys } from './Polyfill';
|
||||
import { PrimaryKey, Sequence } from './DataType';
|
||||
|
||||
type TriggerDataAttributeType = '$$triggerData$$';
|
||||
|
|
@ -22,7 +19,7 @@ export const SeqAttribute = '$$seq$$';
|
|||
export type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType| DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType;
|
||||
export const initinctiveAttributes = [PrimaryKeyAttribute, TriggerDataAttribute, TriggerTimestampAttribute, CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute, SeqAttribute];
|
||||
|
||||
export type Filter<A extends string, F extends Object | undefined = undefined> = {
|
||||
type FilterPart<A extends string, F extends Object | undefined> = {
|
||||
filter?: A extends 'create' ? undefined : F;
|
||||
indexFrom?: A extends 'create' ? undefined : number;
|
||||
count?: A extends 'create' ? undefined : number;
|
||||
|
|
@ -52,21 +49,17 @@ export type FormUpdateData<SH extends GeneralEntityShape> = Partial<{
|
|||
[K in keyof Omit<SH, InstinctiveAttributes>]: SH[K] | null;
|
||||
}>;
|
||||
|
||||
export type FormCreateData<SH extends GeneralEntityShape> = Omit<SH, InstinctiveAttributes> & { id: string };
|
||||
export type FormCreateData<SH extends GeneralEntityShape> = Partial<Omit<SH, InstinctiveAttributes>> & { id: string };
|
||||
|
||||
export type Operation<A extends GenericAction | string,
|
||||
DATA extends Object,
|
||||
FILTER extends Object | undefined = undefined,
|
||||
SORTER extends Object | undefined = undefined> = {
|
||||
export type Operation<A extends string,
|
||||
D extends Projection,
|
||||
F extends Filter | undefined = undefined,
|
||||
S extends Sorter | undefined = undefined> = {
|
||||
id?: string; // 为了一致性,每个operation也应当保证唯一id
|
||||
action: A;
|
||||
data: DATA;
|
||||
sorter?: SORTER;
|
||||
} & Filter<A, FILTER>;
|
||||
|
||||
export type Selection<DATA extends Object,
|
||||
FILTER extends Object | undefined = undefined,
|
||||
SORT extends Object | undefined = undefined> = Operation<'select', DATA, FILTER, SORT>;
|
||||
data: D;
|
||||
sorter?: S;
|
||||
} & FilterPart<A, F>;
|
||||
|
||||
export interface EntityShape {
|
||||
id: PrimaryKey;
|
||||
|
|
@ -76,9 +69,6 @@ export interface EntityShape {
|
|||
$$deleteAt$$?: number | Date | null;
|
||||
}
|
||||
|
||||
export interface FileCarrierEntityShape extends EntityShape {
|
||||
};
|
||||
|
||||
interface GeneralEntityShape extends EntityShape {
|
||||
[K: string]: any;
|
||||
}
|
||||
|
|
@ -91,33 +81,28 @@ export interface EntityDef {
|
|||
OpSchema: GeneralEntityShape;
|
||||
Action: string;
|
||||
ParticularAction?: string;
|
||||
Selection: Omit<DeduceSelection<this['Schema']>, 'action'>;
|
||||
Aggregation: Omit<DeduceAggregation<this['Schema'], DeduceProjection<this['Schema']>, DeduceFilter<this['Schema']>, DeduceSorter<this['Schema']>>, 'action'>;
|
||||
Operation: DeduceOperation<this['Schema']>;
|
||||
Create: DeduceCreateOperation<this['Schema']>;
|
||||
CreateSingle: DeduceCreateSingleOperation<this['Schema']>;
|
||||
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
|
||||
Update: DeduceUpdateOperation<this['Schema']>;
|
||||
Remove: DeduceRemoveOperation<this['Schema']>;
|
||||
Selection: Omit<Operation<'select', Projection, Filter, Sorter>, 'action'>;
|
||||
Aggregation: Omit<DeduceAggregation<Projection, Filter, Sorter>, 'action'>;
|
||||
Operation: CUDOperation;
|
||||
Create: CreateOperation;
|
||||
CreateSingle: CreateSingleOperation;
|
||||
CreateMulti: CreateMultipleOperation;
|
||||
Update: UpdateOperation;
|
||||
Remove: RemoveOperation;
|
||||
Relation?: string;
|
||||
};
|
||||
|
||||
export interface EntityDict {
|
||||
[E: string]: EntityDef;
|
||||
};
|
||||
|
||||
export interface OtmSubProjection extends Omit<DeduceSelection<any>, 'action'> {
|
||||
export interface OtmSubProjection extends Omit<Operation<'select', any, any, any>, 'action'> {
|
||||
$entity: string;
|
||||
};
|
||||
|
||||
type DeduceProjection<SH extends GeneralEntityShape> = {
|
||||
'#id'?: NodeId;
|
||||
} & {
|
||||
[K in keyof SH]?: number | OtmSubProjection | any;
|
||||
} & Partial<ExprOp<keyof SH | string>>;
|
||||
|
||||
export type AggregationOp = `#max-${number}` | `#min-${number}` | `#avg-${number}` | `#count-${number}` | `#sum-${number}`;
|
||||
|
||||
export type DeduceAggregationData<SH extends GeneralEntityShape, P extends DeduceProjection<SH>> = {
|
||||
export type DeduceAggregationData<P extends Projection> = {
|
||||
[A in AggregationOp]?: P;
|
||||
} & {
|
||||
'#aggr'?: P;
|
||||
|
|
@ -130,57 +115,58 @@ export type AggregationResult<SH extends GeneralEntityShape> = Array<{
|
|||
}>;
|
||||
|
||||
export type AttrFilter<SH extends GeneralEntityShape> = {
|
||||
[K in keyof SH]: any;
|
||||
[K in keyof SH]?: any;
|
||||
}
|
||||
|
||||
export type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>;
|
||||
type SortAttr = {
|
||||
[K: string]: any;
|
||||
};
|
||||
|
||||
export type DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{
|
||||
[K: string]: number | object | undefined;
|
||||
} & ExprOp<keyof SH>>;
|
||||
|
||||
export type DeduceSorterItem<SH extends GeneralEntityShape> = {
|
||||
$attr: DeduceSorterAttr<SH>;
|
||||
type SorterItem = {
|
||||
$attr: SortAttr
|
||||
$direction?: "asc" | "desc";
|
||||
};
|
||||
|
||||
export type DeduceSorter<SH extends GeneralEntityShape> = Array<DeduceSorterItem<SH>>;
|
||||
type Sorter = Array<SorterItem>;
|
||||
|
||||
export type DeduceSelection<SH extends GeneralEntityShape> = Selection<DeduceProjection<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
type Filter = {
|
||||
[K: string]: any;
|
||||
}
|
||||
|
||||
type Projection = {
|
||||
[K: string]: any;
|
||||
}
|
||||
|
||||
export type DeduceAggregation<
|
||||
SH extends GeneralEntityShape,
|
||||
P extends DeduceProjection<SH>,
|
||||
F extends DeduceFilter<SH>,
|
||||
S extends DeduceSorter<SH>> = Omit<Operation<'aggregate', DeduceAggregationData<SH, P>, F, S>, 'action'>;
|
||||
P extends Projection,
|
||||
F extends Filter,
|
||||
S extends Sorter> = Omit<Operation<'aggregate', DeduceAggregationData<P>, F, S>, 'action'>;
|
||||
|
||||
export type DeduceCreateOperationData<SH extends GeneralEntityShape> = {
|
||||
type CreateOperationData = {
|
||||
id: string;
|
||||
} & {
|
||||
[k in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
[K: string]: any;
|
||||
};
|
||||
|
||||
export type DeduceCreateSingleOperation<SH extends GeneralEntityShape> = Operation<'create', DeduceCreateOperationData<SH>>;
|
||||
type CreateSingleOperation = Operation<'create', CreateOperationData, undefined, undefined>;
|
||||
|
||||
export type DeduceCreateMultipleOperation<SH extends GeneralEntityShape> = Operation<'create', Array<DeduceCreateOperationData<SH>>>;
|
||||
type CreateMultipleOperation = Operation<'create', Array<CreateOperationData>, undefined, undefined>;
|
||||
|
||||
export type DeduceCreateOperation<SH extends GeneralEntityShape> = DeduceCreateSingleOperation<SH> | DeduceCreateMultipleOperation<SH>;
|
||||
type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
|
||||
|
||||
export type DeduceUpdateOperationData<SH extends GeneralEntityShape> = {
|
||||
[k in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
};
|
||||
type UpdateOperationData = {
|
||||
id?: never;
|
||||
[k: string]: any;
|
||||
}
|
||||
|
||||
export type DeduceUpdateOperation<SH extends GeneralEntityShape> = Operation<
|
||||
'update' | string,
|
||||
DeduceUpdateOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
export type UpdateOperation = Operation<string, UpdateOperationData, Filter, Sorter>;
|
||||
|
||||
export type DeduceRemoveOperationData<SH extends GeneralEntityShape> = {
|
||||
[A in keyof Omit<SH, InstinctiveAttributes>]?: any;
|
||||
};
|
||||
type RemoveOperationData = {
|
||||
[k: string]: any;
|
||||
}
|
||||
|
||||
export type DeduceRemoveOperation<SH extends GeneralEntityShape> = Operation<'remove', DeduceRemoveOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
|
||||
export type RemoveOperation = Operation<'remove', RemoveOperationData, Filter, Sorter>;
|
||||
|
||||
export type DeduceOperation<SH extends GeneralEntityShape> = DeduceCreateOperation<SH> | DeduceUpdateOperation<SH> | DeduceRemoveOperation<SH>;
|
||||
export type CUDOperation = CreateOperation | UpdateOperation | RemoveOperation;
|
||||
|
||||
export type CreateOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'c';
|
||||
|
|
@ -191,20 +177,29 @@ export type CreateOpResult<ED extends EntityDict, T extends keyof ED> = {
|
|||
export type UpdateOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'u',
|
||||
e: T;
|
||||
d: DeduceUpdateOperationData<ED[T]['Schema']>;
|
||||
f?: DeduceFilter<ED[T]['Schema']>;
|
||||
d: UpdateOperationData;
|
||||
f?: Filter;
|
||||
};
|
||||
|
||||
export type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = {
|
||||
a: 'r',
|
||||
e: T;
|
||||
f?: DeduceFilter<ED[T]['Schema']>;
|
||||
f?: Filter;
|
||||
};
|
||||
|
||||
export type RelationHierarchy<R extends string> = {
|
||||
[K in R]?: R[];
|
||||
};
|
||||
|
||||
export type CascadeRelationItem = {
|
||||
cascadePath: string;
|
||||
relations?: string[];
|
||||
};
|
||||
|
||||
export type CascadeRelationAuth<R extends string> = {
|
||||
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
||||
// Select的级联可以去重,压缩返回的数据大小
|
||||
export type SelectOpResult<ED extends EntityDict> = {
|
||||
a: 's',
|
||||
|
|
@ -250,20 +245,3 @@ export type Configuration = {
|
|||
actionType?: ActionType;
|
||||
static?: boolean; // 标识是维表(变动较小,相对独立)
|
||||
};
|
||||
|
||||
export type Exportation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
projection: ED[T]['Selection']['data'];
|
||||
headers: K[];
|
||||
fn: (data: ED[T]['Schema']) => Partial<Record<K, string | number | boolean | null>>;
|
||||
};
|
||||
|
||||
export type Importation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
headers: K[];
|
||||
fn: (data: Partial<Record<K, string | number | boolean>>) => ED[T]['CreateSingle']['data'];
|
||||
};
|
||||
|
|
@ -26,6 +26,18 @@ export class OakDataException extends OakException {
|
|||
// 表示由数据层发现的异常
|
||||
}
|
||||
|
||||
export class OakImportDataParseException extends OakException {
|
||||
line: number;
|
||||
header?: string;
|
||||
|
||||
// message必传,描述具体错误的数据内容
|
||||
constructor(message: string, line: number, header?: string) {
|
||||
super(message);
|
||||
this.line = line;
|
||||
this.header = header;
|
||||
}
|
||||
}
|
||||
|
||||
export class OakOperExistedException extends OakDataException {
|
||||
// 进行操作时发现同样id的Oper对象已经存在
|
||||
}
|
||||
|
|
@ -220,6 +232,9 @@ export function makeException(data: {
|
|||
case 'OakDeadlock': {
|
||||
return new OakDeadlock(data.message);
|
||||
}
|
||||
case 'OakImportDataParseException': {
|
||||
return new OakImportDataParseException(data.message!, data.line, data.header);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export type RefOrExpression<A> = RefAttr<A> | Expression<A>;
|
|||
type MathType<A> = RefOrExpression<A> | number;
|
||||
type StringType<A> = RefOrExpression<A> | string
|
||||
interface Add<A> {
|
||||
$add: (MathType<A> | StringType<A>)[];
|
||||
$add: (MathType<A>)[];
|
||||
};
|
||||
interface Subtract<A> {
|
||||
$subtract: [MathType<A>, MathType<A>];
|
||||
|
|
@ -135,6 +135,15 @@ interface DateFloor<A> {
|
|||
type DateExpression<A> = DateYear<A> | DateMonth<A> | DateWeekday<A> | DateWeekOfYear<A> | DateDay<A> | DateDayOfYear<A>
|
||||
| DateDayOfMonth<A> | DateDayOfWeek<A> | DateDiff<A> | DateCeiling<A> | DateFloor<A>;
|
||||
|
||||
// String
|
||||
interface StringConcat<A> {
|
||||
$concat: StringType<A>[];
|
||||
}
|
||||
|
||||
type StringExpression<A> = StringConcat<A>;
|
||||
|
||||
|
||||
|
||||
//// Geo
|
||||
interface GeoContains<A> {
|
||||
$contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
|
||||
|
|
@ -145,7 +154,31 @@ interface GeoDistance<A> {
|
|||
|
||||
type GeoExpression<A> = GeoContains<A> | GeoDistance<A>;
|
||||
|
||||
export type Expression<A> = GeoExpression<A> | DateExpression<A> | LogicExpression<A> | BoolExpression<A> | CompareExpression<A> | MathExpression<A>;
|
||||
//// Aggr
|
||||
interface AggrCountExpression<A> {
|
||||
$$count: RefOrExpression<A>;
|
||||
};
|
||||
|
||||
interface AggrSumExpression<A> {
|
||||
$$sum: RefOrExpression<A>;
|
||||
}
|
||||
|
||||
interface AggrMaxExpression<A> {
|
||||
$$max: RefOrExpression<A>;
|
||||
}
|
||||
|
||||
interface AggrMinExpression<A> {
|
||||
$$min: RefOrExpression<A>;
|
||||
}
|
||||
|
||||
interface AggrAvgExpression<A> {
|
||||
$$avg: RefOrExpression<A>;
|
||||
}
|
||||
|
||||
export type AggrExpression<A> = AggrAvgExpression<A> | AggrCountExpression<A> | AggrSumExpression<A> | AggrMaxExpression<A> | AggrMinExpression<A>;
|
||||
|
||||
export type Expression<A> = GeoExpression<A> | DateExpression<A> | LogicExpression<A>
|
||||
| BoolExpression<A> | CompareExpression<A> | MathExpression<A> | StringExpression<A> | AggrExpression<A>;
|
||||
|
||||
export type ExpressionConstant = Geo | number | Date | string | boolean;
|
||||
|
||||
|
|
@ -212,13 +245,35 @@ export function isMathExpression<A>(expression: any): expression is MathExpressi
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
export function isStringExpression<A>(expression: any): expression is StringExpression<A> {
|
||||
if (Object.keys(expression).length == 1) {
|
||||
const op = Object.keys(expression)[0];
|
||||
if (['$concat'].includes(op)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isAggrExpression<A>(expression: any): expression is AggrExpression<A> {
|
||||
if (Object.keys(expression).length == 1) {
|
||||
const op = Object.keys(expression)[0];
|
||||
if (['$$max', '$$min', '$$sum', '$$avg', '$$count'].includes(op)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isExpression<A>(expression: any): expression is Expression<A> {
|
||||
return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$');
|
||||
}
|
||||
|
||||
export function opMultipleParams(op: string) {
|
||||
return !['$year', '$month', '$weekday', '$weekOfYear', '$day', '$dayOfMonth',
|
||||
'$dayOfWeek', '$dayOfYear', '$not', '$true', '$false', '$abs', '$round', '$floor', '$ceil'].includes(op);
|
||||
'$dayOfWeek', '$dayOfYear', '$not', '$true', '$false', '$abs',
|
||||
'$round', '$floor', '$ceil', '$$max', '$$min', '$$sum', '$$avg', '$$count'].includes(op);
|
||||
}
|
||||
|
||||
export function execOp(op: string, params: any, obscure?: boolean): ExpressionConstant {
|
||||
|
|
@ -435,6 +490,9 @@ export function execOp(op: string, params: any, obscure?: boolean): ExpressionCo
|
|||
case '$contains': {
|
||||
throw new Error('$contains类型未实现');
|
||||
}
|
||||
case '$concat': {
|
||||
return params.join('');
|
||||
}
|
||||
default: {
|
||||
assert(false, `不能识别的expression运算符:${op}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
import { EntityDict } from "./Entity";
|
||||
|
||||
export type Exportation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
projection: ED[T]['Selection']['data'];
|
||||
headers: K[];
|
||||
fn: (data: ED[T]['Schema']) => Partial<Record<K, string | number | boolean | null>>;
|
||||
};
|
||||
|
||||
export type Importation<ED extends EntityDict, T extends keyof ED, K extends string> = {
|
||||
name: string;
|
||||
id: string;
|
||||
entity: T;
|
||||
headers: K[];
|
||||
// 解析过程中如果出错,请抛出OakImportDataParseException异常
|
||||
fn: (data: Partial<Record<K, string | number | boolean>>[], context: AsyncContext<ED>, option?: Record<string, any> ) => Promise<ED[T]['CreateMulti']['data']>;
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { ActionType } from '.';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy } from './Entity';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, CascadeRelationAuth } from './Entity';
|
||||
import { DataType, DataTypeParams } from './schema/DataTypes';
|
||||
|
||||
export type Ref = 'ref';
|
||||
|
|
@ -47,7 +47,7 @@ export type UniqConstraint<SH extends EntityShape> = {
|
|||
type?: string;
|
||||
};
|
||||
|
||||
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> {
|
||||
export interface StorageDesc<SH extends EntityShape> {
|
||||
storageName?: string,
|
||||
comment?: string,
|
||||
attributes: Attributes<SH>;
|
||||
|
|
@ -59,12 +59,14 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
|
|||
static?: true; // 标识是维表(变动较小,相对独立)
|
||||
actions: string[];
|
||||
actionType: ActionType;
|
||||
relationHierarchy?: RelationHierarchy<Relation>;
|
||||
// relationHierarchy?: RelationHierarchy<Relation>;
|
||||
// reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
|
||||
relation?: string[];
|
||||
// view 相关
|
||||
view?: true;
|
||||
}
|
||||
|
||||
|
||||
export type StorageSchema<ED extends EntityDict> = {
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>;
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export interface UpdateTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
|
|||
attributes?: keyof ED[T]['OpSchema'] | Array<keyof ED[T]['OpSchema']>;
|
||||
check?: (operation: ED[T]['Update']) => boolean;
|
||||
fn: (event: { operation: ED[T]['Update'] }, context: Cxt, option: OperateOption) => Promise<number> | number;
|
||||
filter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Update'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
|
||||
filter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Update'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Update']['filter']>);
|
||||
};
|
||||
|
||||
export interface UpdateTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends UpdateTriggerBase<ED, T, Cxt> {
|
||||
|
|
@ -63,7 +63,7 @@ export interface RemoveTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
|
|||
action: 'remove',
|
||||
check?: (operation: ED[T]['Remove']) => boolean;
|
||||
fn: (event: { operation: ED[T]['Remove'] }, context: Cxt, option: OperateOption) => Promise<number> | number;
|
||||
filter?: ED[T]['Remove']['filter'] | ((operation: ED[T]['Remove'], context: Cxt, option: OperateOption) => ED[T]['Remove']['filter']);
|
||||
filter?: ED[T]['Remove']['filter'] | ((operation: ED[T]['Remove'], context: Cxt, option: OperateOption) => ED[T]['Remove']['filter'] | Promise<ED[T]['Remove']['filter']>);
|
||||
};
|
||||
|
||||
export interface RemoveTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt> {
|
||||
|
|
|
|||
|
|
@ -17,3 +17,5 @@ export * from './Watcher';
|
|||
export * from './AppLoader';
|
||||
export * from './Connector';
|
||||
export * from './Timer';
|
||||
export * from './Port';
|
||||
export * from './Endpoint';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
export function excelStringToDate(str: string | number) {
|
||||
if (!str) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof str === 'number') {
|
||||
if (str < 100000) {
|
||||
return dayjs((((str - 25569) * 24 - 8) * 3600) * 1000).valueOf(); // excel日期可能为1900-1-1至今的天数
|
||||
}
|
||||
return dayjs(str).valueOf();
|
||||
}
|
||||
return Date.parse(str);
|
||||
}
|
||||
Loading…
Reference in New Issue