Merge branch 'release'

This commit is contained in:
Xu Chang 2023-01-20 19:43:34 +08:00
commit 12ca91e37f
59 changed files with 1621 additions and 919 deletions

View File

@ -1 +1,5 @@
import { CascadeRelationItem, RelationHierarchy } from "../types/Entity";
export declare type GenericRelation = 'owner'; export declare type GenericRelation = 'owner';
export declare function convertHierarchyToAuth<R extends string>(hierarchy: RelationHierarchy<R>): {
[K in R]?: CascadeRelationItem;
};

View File

@ -1,2 +1,38 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); 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;

View File

@ -107,7 +107,7 @@ export declare type SortNode = {
export declare type Sorter = SortNode[]; export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">; 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 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">> & ({ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId">> & ({
entity?: string; entity?: string;
entityId?: 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 UpdateOperation = OakOperation<"update" | ParticularAction | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {}; export declare type RemoveOperationData = {};
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; 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 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 = { export declare type EntityDef = {
Schema: Schema; Schema: Schema;
OpSchema: OpSchema; OpSchema: OpSchema;

View File

@ -83,7 +83,7 @@ export declare type SortNode = {
export declare type Sorter = SortNode[]; export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">; 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 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">> & (({ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId" | "modiId">> & (({
modiId?: never; modiId?: never;
modi: Modi.CreateSingleOperation; modi: Modi.CreateSingleOperation;
@ -142,12 +142,10 @@ export declare type RemoveOperationData = {} & (({
[k: string]: any; [k: string]: any;
}); });
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; 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 ModiIdSubQuery = Selection<ModiIdProjection>;
export declare type UserIdSubQuery = Selection<UserIdProjection>; export declare type UserIdSubQuery = Selection<UserIdProjection>;
export declare type ModiEntityIdSubQuery = Selection<ModiEntityIdProjection>; 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 = { export declare type EntityDef = {
Schema: Schema; Schema: Schema;
OpSchema: OpSchema; OpSchema: OpSchema;

View File

@ -89,7 +89,7 @@ export declare type SortNode = {
export declare type Sorter = SortNode[]; export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">; 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 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">> & (({ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "operatorId">> & (({
operatorId?: never; operatorId?: never;
operator?: User.CreateSingleOperation; operator?: User.CreateSingleOperation;
@ -125,11 +125,9 @@ export declare type RemoveOperationData = {} & (({
operator?: User.UpdateOperation | User.RemoveOperation; operator?: User.UpdateOperation | User.RemoveOperation;
})); }));
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; 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 UserIdSubQuery = Selection<UserIdProjection>;
export declare type OperIdSubQuery = Selection<OperIdProjection>; 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 = { export declare type EntityDef = {
Schema: Schema; Schema: Schema;
OpSchema: OpSchema; OpSchema: OpSchema;

View File

@ -92,7 +92,7 @@ export declare type SortNode = {
export declare type Sorter = SortNode[]; export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">; 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 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">> & (({ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity" | "entityId" | "operId">> & (({
operId?: never; operId?: never;
oper: Oper.CreateSingleOperation; oper: Oper.CreateSingleOperation;
@ -157,13 +157,11 @@ export declare type RemoveOperationData = {} & ({
[k: string]: any; [k: string]: any;
}); });
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; 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 OperIdSubQuery = Selection<OperIdProjection>;
export declare type ModiIdSubQuery = Selection<ModiIdProjection>; export declare type ModiIdSubQuery = Selection<ModiIdProjection>;
export declare type UserIdSubQuery = Selection<UserIdProjection>; export declare type UserIdSubQuery = Selection<UserIdProjection>;
export declare type OperEntityIdSubQuery = Selection<OperEntityIdProjection>; 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 = { export declare type EntityDef = {
Schema: Schema; Schema: Schema;
OpSchema: OpSchema; OpSchema: OpSchema;

View File

@ -92,7 +92,7 @@ export declare type SortNode = {
export declare type Sorter = SortNode[]; export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">; 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 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> & { export declare type CreateOperationData = FormCreateData<OpSchema> & {
oper$operator?: OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">[]> | Array<OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">>>; 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">>>; 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 UpdateOperation = OakOperation<"update" | RelationAction | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {}; export declare type RemoveOperationData = {};
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; 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 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 = { export declare type EntityDef = {
Schema: Schema; Schema: Schema;
OpSchema: OpSchema; OpSchema: OpSchema;

View File

@ -1,5 +1,5 @@
import { EntityDict } from '../base-app-domain'; import { EntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore'; import { AsyncContext } from '../store/AsyncRowStore';
import { SyncContext } from '../store/SyncRowStore'; import { SyncContext } from '../store/SyncRowStore';
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types'; 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>): Checker<ED, keyof ED, Cxt>[]; 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>[];

View File

@ -4,10 +4,12 @@ exports.createDynamicCheckers = void 0;
var tslib_1 = require("tslib"); var tslib_1 = require("tslib");
var checker_1 = require("../store/checker"); var checker_1 = require("../store/checker");
var modi_1 = require("../store/modi"); var modi_1 = require("../store/modi");
function createDynamicCheckers(schema) { function createDynamicCheckers(schema, authDict) {
var checkers = []; var checkers = [];
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false)); checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
checkers.push.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; return checkers;
} }
exports.createDynamicCheckers = createDynamicCheckers; exports.createDynamicCheckers = createDynamicCheckers;

View File

@ -337,7 +337,8 @@ function analyzeEntity(filename, path, program, relativePath) {
var localEnumStringTypes = []; var localEnumStringTypes = [];
var additionalImports = []; var additionalImports = [];
var localeDef = undefined; var localeDef = undefined;
var relationHierarchy = undefined; // let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
ts.forEachChild(sourceFile, function (node) { ts.forEachChild(sourceFile, function (node) {
var _a, _b, _c, _d; var _a, _b, _c, _d;
if (ts.isImportDeclaration(node)) { if (ts.isImportDeclaration(node)) {
@ -795,15 +796,22 @@ function analyzeEntity(filename, path, program, relativePath) {
_static = true; // static如果有值只能为true _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 // RelationHierary
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728RelationHierarchy\u4E4B\u540E")); assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
var initializer = declaration.initializer; const { initializer } = declaration;
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress")); assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
relationHierarchy = initializer; 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 { 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, locale: localeDef,
}); });
} }
if (hasRelationDef) { /* if (hasRelationDef) {
(0, assert_1.default)(relationHierarchy, "".concat(filename, "\u4E2D\u7F3A\u5C11\u4E86relationHierarchy\u5B9A\u4E49")); if(!relationHierarchy && !reverseCascadeRelationHierarchy){
(0, lodash_1.assign)(schema, { console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`);
relationHierarchy: relationHierarchy, }
}); if (relationHierarchy) {
assign(schema, {
relationHierarchy,
});
}
if (reverseCascadeRelationHierarchy) {
assign(schema, {
reverseCascadeRelationHierarchy,
});
}
} }
else { 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 = {}, (0, lodash_1.assign)(Schema, (_a = {},
_a[moduleName] = schema, _a[moduleName] = schema,
_a)); _a));
@ -1783,7 +1801,6 @@ function constructActions(statements, entity) {
factory.createLiteralTypeNode(factory.createStringLiteral("action")) factory.createLiteralTypeNode(factory.createStringLiteral("action"))
])), factory.createTypeAliasDeclaration(undefined, [factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [ ])), 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("DeduceAggregation"), [
factory.createTypeReferenceNode(factory.createIdentifier("Schema"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined) factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
@ -1939,6 +1956,11 @@ function constructActions(statements, entity) {
var reverseOneNodes = []; var reverseOneNodes = [];
if (ReversePointerEntities[entity]) { if (ReversePointerEntities[entity]) {
if (ReversePointerRelations[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 { try {
for (var _q = tslib_1.__values(ReversePointerRelations[entity]), _r = _q.next(); !_r.done; _r = _q.next()) { for (var _q = tslib_1.__values(ReversePointerRelations[entity]), _r = _q.next(); !_r.done; _r = _q.next()) {
var one = _r.value; 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'))) factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), undefined, factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')))
]); ]);
var noCascadeNode = factory.createTypeLiteralNode([ var noCascadeNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc factory.createPropertySignature(undefined, factory.createIdentifier('entity'), questionToken, factory.createLiteralTypeNode(factory.createStringLiteral("".concat((0, string_1.firstLetterLowerCase)(one))))),
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"))]))
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc
factory.createTypeReferenceNode(factory.createIdentifier("String"), [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))]))
]); ]);
if (Schema[one].static) { if (Schema[one].static) {
reverseOneNodes.push(noCascadeNode); 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([ 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("CreateOperation"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperation"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperation"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined)
factory.createTypeReferenceNode(factory.createIdentifier("SelectOperation"), undefined)
]))); ])));
} }
var initialStatements = function () { return [ var initialStatements = function () { return [
@ -2642,7 +2661,6 @@ var initialStatements = function () { return [
function outputSubQuery(outputDir, printer) { function outputSubQuery(outputDir, printer) {
var statements = []; var statements = [];
if (process.env.COMPLING_AS_LIB) { 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) { for (var entity in Schema) {
// import * as User from '../User/Schema'; // import * as User from '../User/Schema';
@ -2755,7 +2773,8 @@ function outputSchema(outputDir, printer) {
constructSorter(statements, entity); constructSorter(statements, entity);
constructActions(statements, entity); constructActions(statements, entity);
constructQuery(statements, entity); constructQuery(statements, entity);
constructFullAttrs(statements, entity); // 现在FullAttrs和NativeAttrs似乎没什么用还会引起递归
// constructFullAttrs(statements, entity);
var makeActionArguments = []; var makeActionArguments = [];
if (ActionAsts[entity]) { if (ActionAsts[entity]) {
makeActionArguments.push(factory.createTypeReferenceNode('Action')); makeActionArguments.push(factory.createTypeReferenceNode('Action'));
@ -2787,6 +2806,9 @@ function outputSchema(outputDir, printer) {
if (ActionAsts[entity]) { if (ActionAsts[entity]) {
EntityDefAttrs.push(factory.createPropertySignature(undefined, factory.createIdentifier("ParticularAction"), undefined, factory.createTypeReferenceNode(factory.createIdentifier('ParticularAction'), undefined))); 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))); 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 result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), Schema[entity].sourceFile);
var fileName = path_1.default.join(outputDir, entity, 'Schema.ts'); var fileName = path_1.default.join(outputDir, entity, 'Schema.ts');
@ -3045,13 +3067,19 @@ function outputStorage(outputDir, printer) {
var entityAssignments = []; var entityAssignments = [];
for (var entity in Schema) { for (var entity in Schema) {
var indexExpressions = []; 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 = [ var fromSchemaSpecifiers = [
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema")) factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
]; ];
if (relationHierarchy) { /* if (relationHierarchy || reverseCascadeRelationHierarchy) {
fromSchemaSpecifiers.push(factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation"))); fromSchemaSpecifiers.push(
} factory.createImportSpecifier(
false,
undefined,
factory.createIdentifier("Relation")
)
);
} */
var statements = [ 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([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) 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) { if (indexExpressions.length > 0) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true))); propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true)));
} }
if (relationHierarchy) { /* if (relationHierarchy) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relationHierarchy"), 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 = [ var sdTypeArguments = [
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined) 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))); 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 result_2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile);
var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts'); var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts');

View File

@ -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 { EntityDict as BaseEntityDict } from '../base-app-domain';
import { RowStore } from '../types/RowStore'; import { RowStore } from '../types/RowStore';
import { StorageSchema } from '../types/Storage'; import { StorageSchema } from '../types/Storage';
@ -51,14 +51,14 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
* @param filter * @param filter
* @returns * @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>; data: Record<string, any>;
beforeFns: (() => R)[]; beforeFns: (() => R)[];
afterFns: (() => R)[]; afterFns: (() => R)[];
}; };
protected preProcessDataCreated<T extends keyof ED>(entity: T, data: ED[T]['Create']['data']): void; 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; protected preProcessDataUpdated(data: Record<string, any>): void;
judgeRelation(entity: keyof ED, attr: string): string | 2 | 1 | string[] | 0; judgeRelation(entity: keyof ED, attr: string): string | 1 | 2 | string[] | 0;
/** /**
* update过程无关的例程放在这里later动作的处理oper的记录以及对record的收集等 * update过程无关的例程放在这里later动作的处理oper的记录以及对record的收集等
* @param entity * @param entity

View File

@ -32,15 +32,15 @@ var TriggerExecutor = /** @class */ (function () {
TriggerExecutor.prototype.registerChecker = function (checker) { TriggerExecutor.prototype.registerChecker = function (checker) {
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter; var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++); var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
var fn = (0, checker_1.translateCheckerInAsyncContext)(checker); var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
var trigger = { var trigger = {
checkerType: type, checkerType: type,
name: triggerName, name: triggerName,
priority: checker.priority || 2, priority: checker.priority || 20,
entity: entity, entity: entity,
action: action, action: action,
fn: fn, fn: fn,
when: 'before', when: when,
filter: conditionalFilter, filter: conditionalFilter,
}; };
this.registerTrigger(trigger); 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")); throw new Error("\u4E0D\u53EF\u6709\u540C\u540D\u7684\u89E6\u53D1\u5668\u300C".concat(trigger.name, "\u300D"));
} }
if (typeof trigger.priority !== 'number') { if (typeof trigger.priority !== 'number') {
trigger.priority = 1; // 默认最低 trigger.priority = 10; // 默认值
} }
if (trigger.filter) { if (trigger.filter) {
(0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create' (0, assert_1.default)(typeof trigger.action === 'string' && trigger.action !== 'create'
@ -205,6 +205,7 @@ var TriggerExecutor = /** @class */ (function () {
(0, assert_1.default)(operation.action !== 'create'); (0, assert_1.default)(operation.action !== 'create');
var filter = trigger.filter; var filter = trigger.filter;
var filterr = typeof filter === 'function' ? filter(operation, context, option) : 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); var filterRepelled = (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter);
if (filterRepelled) { if (filterRepelled) {
continue; continue;
@ -228,28 +229,37 @@ var TriggerExecutor = /** @class */ (function () {
else { else {
// 异步context // 异步context
var execPreTrigger_1 = function (idx) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var execPreTrigger_1 = function (idx) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var trigger, filter, filterr, filterRepelled, number; var trigger, filter, filterr, _a, filterRepelled, number;
return tslib_1.__generator(this, function (_a) { return tslib_1.__generator(this, function (_b) {
switch (_a.label) { switch (_b.label) {
case 0: case 0:
if (idx >= preTriggers_2.length) { if (idx >= preTriggers_2.length) {
return [2 /*return*/]; return [2 /*return*/];
} }
trigger = preTriggers_2[idx]; 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'); (0, assert_1.default)(operation.action !== 'create');
filter = trigger.filter; filter = trigger.filter;
filterr = typeof filter === 'function' ? filter(operation, context, option) : filter; if (!(typeof filter === 'function')) return [3 /*break*/, 2];
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)]; return [4 /*yield*/, filter(operation, context, option)];
case 1: 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) { if (filterRepelled) {
return [2 /*return*/, execPreTrigger_1(idx + 1)]; return [2 /*return*/, execPreTrigger_1(idx + 1)];
} }
_a.label = 2; _b.label = 5;
case 2: return [4 /*yield*/, trigger.fn({ operation: operation }, context, option)]; case 5: return [4 /*yield*/, trigger.fn({ operation: operation }, context, option)];
case 3: case 6:
number = _a.sent(); number = _b.sent();
if (number > 0) { 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")); 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 execCommitTrigger_1 = function (idx) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var trigger, filter, filterr, filterRepelled; var trigger, filter, filterr, _a, filterRepelled;
return tslib_1.__generator(this, function (_a) { return tslib_1.__generator(this, function (_b) {
switch (_a.label) { switch (_b.label) {
case 0: case 0:
if (idx >= commitTriggers_1.length) { if (idx >= commitTriggers_1.length) {
return [2 /*return*/]; return [2 /*return*/];
} }
trigger = commitTriggers_1[idx]; 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'); (0, assert_1.default)(operation.action !== 'create');
filter = trigger.filter; filter = trigger.filter;
filterr = typeof filter === 'function' ? filter(operation, context, option) : filter; if (!(typeof filter === 'function')) return [3 /*break*/, 2];
return [4 /*yield*/, (0, filter_1.checkFilterRepel)(entity, context, filterr, operation.filter)]; return [4 /*yield*/, filter(operation, context, option)];
case 1: 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) { if (filterRepelled) {
return [2 /*return*/, execCommitTrigger_1(idx + 1)]; return [2 /*return*/, execCommitTrigger_1(idx + 1)];
} }
_a.label = 2; _b.label = 5;
case 2: return [4 /*yield*/, this.preCommitTrigger(entity, operation, trigger, context, option)]; case 5: return [4 /*yield*/, this.preCommitTrigger(entity, operation, trigger, context, option)];
case 3: case 6:
_a.sent(); _b.sent();
return [2 /*return*/, execCommitTrigger_1(idx + 1)]; return [2 /*return*/, execCommitTrigger_1(idx + 1)];
} }
}); });

View File

@ -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 { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from "./AsyncRowStore"; import { AsyncContext } from "./AsyncRowStore";
import { SyncContext } from './SyncRowStore'; import { SyncContext } from './SyncRowStore';
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn']; export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): {
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; fn: Trigger<ED, keyof ED, Cxt>['fn'];
export declare function createRelationHierarchyCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[]; 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>[];

View File

@ -1,58 +1,79 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); 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 tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert")); var assert_1 = tslib_1.__importDefault(require("assert"));
var filter_1 = require("../store/filter"); var filter_1 = require("../store/filter");
var Exception_1 = require("../types/Exception"); var Exception_1 = require("../types/Exception");
var actionDef_1 = require("./actionDef"); var actionDef_1 = require("./actionDef");
var string_1 = require("../utils/string"); var string_1 = require("../utils/string");
var lodash_1 = require("../utils/lodash");
var relation_1 = require("./relation");
function translateCheckerInAsyncContext(checker) { function translateCheckerInAsyncContext(checker) {
var _this = this; 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) { switch (type) {
case 'data': { case 'data': {
var checkerFn_1 = checker.checker; var checkerFn_1 = checker.checker;
return (function (_a, context) { var fn = (function (_a, context) {
var operation = _a.operation; var operation = _a.operation;
return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var data; var data;
return tslib_1.__generator(this, function (_b) { return tslib_1.__generator(this, function (_b) {
data = operation.data; switch (_b.label) {
checkerFn_1(data, context); case 0:
return [2 /*return*/, 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': { case 'row': {
var filter_2 = checker.filter, errMsg_1 = checker.errMsg, inconsistentRows_1 = checker.inconsistentRows; 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; var operation = _a.operation;
return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var operationFilter, action, filter2, entity2, selection2, rows2, data_1, rows2, data_2; var operationFilter, action, filter2, _b, entity2, selection2, rows2, data_1, rows2, data_2;
var _b, _c; var _c, _d;
return tslib_1.__generator(this, function (_d) { return tslib_1.__generator(this, function (_e) {
switch (_d.label) { switch (_e.label) {
case 0: case 0:
operationFilter = operation.filter, action = operation.action; operationFilter = operation.filter, action = operation.action;
filter2 = typeof filter_2 === 'function' ? filter_2(operation, context, option) : filter_2; if (!(typeof filter_2 === 'function')) return [3 /*break*/, 2];
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 1]; 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); operation.filter = (0, filter_1.addFilterSegment)(operationFilter || {}, filter2);
return [2 /*return*/, 0]; return [2 /*return*/, 0];
case 1: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {})]; case 4: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)];
case 2: case 5:
if (_d.sent()) { if (_e.sent()) {
return [2 /*return*/, 0]; 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; entity2 = inconsistentRows_1.entity, selection2 = inconsistentRows_1.selection;
return [4 /*yield*/, context.select(entity2, selection2(operationFilter), { return [4 /*yield*/, context.select(entity2, selection2(operationFilter), {
dontCollect: true, dontCollect: true,
blockTrigger: true, blockTrigger: true,
})]; })];
case 3: case 6:
rows2 = _d.sent(); rows2 = _e.sent();
data_1 = {}; data_1 = {};
rows2.forEach(function (ele) { rows2.forEach(function (ele) {
var _a; var _a;
@ -62,11 +83,11 @@ function translateCheckerInAsyncContext(checker) {
}); });
throw new Exception_1.OakRowInconsistencyException({ throw new Exception_1.OakRowInconsistencyException({
a: 's', a: 's',
d: (_b = {}, d: (_c = {},
_b[entity2] = data_1, _c[entity2] = data_1,
_b) _c)
}, errMsg_1); }, 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()), data: (0, actionDef_1.getFullProjection)(entity, context.getSchema()),
filter: Object.assign({}, operationFilter, { filter: Object.assign({}, operationFilter, {
$not: filter2, $not: filter2,
@ -75,8 +96,8 @@ function translateCheckerInAsyncContext(checker) {
dontCollect: true, dontCollect: true,
blockTrigger: true, blockTrigger: true,
})]; })];
case 5: case 8:
rows2 = _d.sent(); rows2 = _e.sent();
data_2 = {}; data_2 = {};
rows2.forEach(function (ele) { rows2.forEach(function (ele) {
var _a; var _a;
@ -86,70 +107,91 @@ function translateCheckerInAsyncContext(checker) {
}); });
throw new Exception_1.OakRowInconsistencyException({ throw new Exception_1.OakRowInconsistencyException({
a: 's', a: 's',
d: (_c = {}, d: (_d = {},
_c[entity] = data_2, _d[entity] = data_2,
_c) _d)
}, errMsg_1); }, errMsg_1);
} }
}); });
}); });
}); });
return {
fn: fn,
when: when,
};
} }
case 'relation': { case 'relation': {
var relationFilter_1 = checker.relationFilter; var relationFilter_1 = checker.relationFilter, errMsg_2 = checker.errMsg;
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 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; var operation = _a.operation;
return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_b) { return tslib_1.__generator(this, function (_b) {
if (context.isRoot()) { switch (_b.label) {
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) {
case 0: case 0:
if (context.isRoot() && type === 'expressionRelation') { if (context.isRoot() && type === 'logicalRelation') {
return [2 /*return*/, 0]; return [2 /*return*/, 0];
} }
exprResult = expression_1(operation, context, option); return [4 /*yield*/, checkerFn_2(operation, context, option)];
if (!(typeof exprResult === 'string')) return [3 /*break*/, 1];
throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_2);
case 1: case 1:
if (!(exprResult === undefined)) return [3 /*break*/, 2]; _b.sent();
return [2 /*return*/, 0]; 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: { default: {
(0, assert_1.default)(false); (0, assert_1.default)(false);
@ -158,15 +200,20 @@ function translateCheckerInAsyncContext(checker) {
} }
exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext; exports.translateCheckerInAsyncContext = translateCheckerInAsyncContext;
function translateCheckerInSyncContext(checker) { 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) { switch (type) {
case 'data': { case 'data': {
var checkerFn_2 = checker.checker; var checkerFn_3 = checker.checker;
return function (operation, context) { return checkerFn_2(operation.data, context); }; var fn = function (operation, context) { return checkerFn_3(operation.data, context); };
return {
fn: fn,
when: when,
};
} }
case 'row': { case 'row': {
var filter_3 = checker.filter, errMsg_3 = checker.errMsg; 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 operationFilter = operation.filter, action = operation.action;
var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3; var filter2 = typeof filter_3 === 'function' ? filter_3(operation, context, option) : filter_3;
(0, assert_1.default)(operationFilter); (0, assert_1.default)(operationFilter);
@ -175,56 +222,59 @@ function translateCheckerInSyncContext(checker) {
return 0; return 0;
} }
else { 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; return;
} }
throw new Exception_1.OakRowInconsistencyException(undefined, errMsg_3); throw new Exception_1.OakRowInconsistencyException(undefined, errMsg_3);
} }
}; };
return {
fn: fn,
when: when,
};
} }
case 'relation': { case 'relation': {
var filter_4 = checker.relationFilter, errMsg_4 = checker.errMsg; var relationFilter_2 = checker.relationFilter, errMsg_4 = checker.errMsg;
return function (operation, context, option) { var fn = function (operation, context, option) {
if (context.isRoot()) { if (context.isRoot()) {
return; return;
} }
var filter2 = typeof filter_4 === 'function' ? filter_4(operation, context, option) : filter_4; var filter2 = typeof relationFilter_2 === 'function' ? relationFilter_2(operation, context, option) : relationFilter_2;
var operationFilter = operation.filter; var filter = operation.filter, action = operation.action;
(0, assert_1.default)(operationFilter); var filter3 = filter;
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter)) { 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; return;
} }
throw new Exception_1.OakUserUnpermittedException(errMsg_4); throw new Exception_1.OakUserUnpermittedException(errMsg_4);
}; };
return {
fn: fn,
when: when,
};
} }
case 'expression': case 'logical':
case 'expressionRelation': { case 'logicalRelation': {
var expression_2 = checker.expression, errMsg_5 = checker.errMsg; var checkerFn_4 = checker.checker;
return function (operation, context, option) { var fn = function (operation, context, option) {
if (context.isRoot() && type === 'expressionRelation') { if (context.isRoot() && type === 'logicalRelation') {
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);
}
return; return;
} }
checkerFn_4(operation, context, option);
};
return {
fn: fn,
when: when,
}; };
} }
default: { default: {
@ -233,154 +283,211 @@ function translateCheckerInSyncContext(checker) {
} }
} }
exports.translateCheckerInSyncContext = translateCheckerInSyncContext; 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 checkers = [];
var _loop_1 = function (entity) { var _loop_1 = function (entity) {
var e_1, _a; var _a;
var relationHierarchy = schema[entity].relationHierarchy; if (authDict[entity]) {
if (relationHierarchy) { var _b = authDict[entity], relationAuth = _b.relationAuth, actionAuth = _b.actionAuth;
// 先build反向hierarchy的map if (relationAuth) {
var reverseHierarchy_1 = {}; var raFilterMakerDict_1 = {};
for (var r in relationHierarchy) { for (var r in relationAuth) {
try { Object.assign(raFilterMakerDict_1, (_a = {},
for (var _b = (e_1 = void 0, tslib_1.__values(relationHierarchy[r])), _c = _b.next(); !_c.done; _c = _b.next()) { _a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
var r2 = _c.value; _a));
if (!reverseHierarchy_1[r2]) {
reverseHierarchy_1[r2] = [r];
}
else {
reverseHierarchy_1[r2].push(r);
}
}
} }
catch (e_1_1) { e_1 = { error: e_1_1 }; } var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
finally { var entityIdAttr_1 = "".concat(entity, "Id");
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) {
checkers.push({ checkers.push({
entity: userEntityName_1, entity: userEntityName_1,
action: 'remove', action: 'create',
type: 'expressionRelation', type: 'relation',
conditionalFilter: { relationFilter: function (operation, context) {
relation: r, var _a;
}, var data = operation.data;
expression: function (operation, context) { (0, assert_1.default)(!(data instanceof Array));
var _a, _b; var _b = data, relation = _b.relation, _c = entityIdAttr_1, entityId = _b[_c];
var userId = context.getCurrentUserId(); var userId = context.getCurrentUserId();
var filter = operation.filter; if (!raFilterMakerDict_1[relation]) {
var legalRelations = reverseHierarchy_1[r]; return;
if (legalRelations.length === 0) {
return '这是不应该跑出来的情况,请杀程序员祭天';
} }
return { var filter = raFilterMakerDict_1[relation](userId);
entity: userEntityName_1, return _a = {},
expr: { _a[entity] = filter,
$gt: [{ _a;
'#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),
};
}, },
errMsg: '越权操作', errMsg: '越权操作',
}); });
}; checkers.push({
for (var r in reverseHierarchy_1) { entity: userEntityName_1,
_loop_2(r); action: 'remove',
} type: 'relation',
/* // 一个人不能授权给自己也不能删除自己的授权 relationFilter: function (operation, context) {
checkers.push({ var _a;
entity: userEntityName as keyof ED, var userId = context.getCurrentUserId();
action: 'create' as ED[keyof ED]['Action'], var filter = operation.filter;
type: 'data', var makeFilterFromRows = function (rows) {
checker: (data, context) => { var relations = (0, lodash_1.uniq)(rows.map(function (ele) { return ele.relation; }));
assert(!(data instanceof Array)); var entityIds = (0, lodash_1.uniq)(rows.map(function (ele) { return ele[entityIdAttr_1]; }));
const { userId } = data as ED[keyof ED]['CreateSingle']['data']; (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 userId2 = context.getCurrentUserId(true); // const entityId = entityIds[0]!;
if (userId === userId2) { // 所有的relation条件要同时满足and关系注意这里的filter翻译出来是在entity对象上不是在userEntity对象上
throw new OakDataException('不允许授权给自己'); 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),
checkers.push({ _a);
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,
},
}; };
} var toBeRemoved = context.select(userEntityName_1, {
console.warn(`没有当前用户但在删除权限,请检查。对象是${entity}`); data: (_a = {
return {}; id: 1,
}, relation: 1
errMsg: '不允许回收自己的授权', },
}); */ _a[entityIdAttr_1] = 1,
// 转让权限现在用update动作只允许update userId给其它人 _a),
// todo 等实现的时候再写 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) { for (var entity in schema) {
@ -388,4 +495,4 @@ function createRelationHierarchyCheckers(schema) {
} }
return checkers; return checkers;
} }
exports.createRelationHierarchyCheckers = createRelationHierarchyCheckers; exports.createAuthCheckers = createAuthCheckers;

13
lib/store/filter.d.ts vendored
View File

@ -96,5 +96,14 @@ export declare function makeTreeAncestorFilter<ED extends EntityDict, T extends
* @param level * @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 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是否包含containedfilter查询的数据一定满足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>;

View File

@ -828,7 +828,16 @@ function makeTreeDescendantFilter(entity, parentKey, filter, level, includeAll,
return currentLevelInFilter; return currentLevelInFilter;
} }
exports.makeTreeDescendantFilter = makeTreeDescendantFilter; exports.makeTreeDescendantFilter = makeTreeDescendantFilter;
function checkFilterContains(entity, context, contained, filter) { /**
* 检查filter是否包含containedfilter查询的数据一定满足contained
* @param entity
* @param context
* @param contained
* @param filter
* @param dataCompare
* @returns
*/
function checkFilterContains(entity, context, contained, filter, dataCompare) {
if (!filter) { if (!filter) {
throw new types_1.OakRowInconsistencyException(); throw new types_1.OakRowInconsistencyException();
} }
@ -837,23 +846,26 @@ function checkFilterContains(entity, context, contained, filter) {
if (contains(entity, schema, filter, contained)) { if (contains(entity, schema, filter, contained)) {
return true; return true;
} }
// 再判断加上了conditionalFilter后取得的行数是否缩减 if (dataCompare) {
var filter2 = combineFilters([filter, { // 再判断加上了conditionalFilter后取得的行数是否缩减
$not: contained, var filter2 = combineFilters([filter, {
}]); $not: contained,
var count = context.count(entity, { }]);
filter: filter2, var count = context.count(entity, {
}, { filter: filter2,
dontCollect: true, }, {
blockTrigger: true, dontCollect: true,
}); blockTrigger: true,
if (count instanceof Promise) { });
return count.then(function (count2) { return count2 === 0; }); if (count instanceof Promise) {
return count.then(function (count2) { return count2 === 0; });
}
return count === 0;
} }
return count === 0; return false;
} }
exports.checkFilterContains = checkFilterContains; exports.checkFilterContains = checkFilterContains;
function checkFilterRepel(entity, context, filter1, filter2) { function checkFilterRepel(entity, context, filter1, filter2, dataCompare) {
if (!filter2) { if (!filter2) {
throw new types_1.OakRowInconsistencyException(); throw new types_1.OakRowInconsistencyException();
} }
@ -863,16 +875,19 @@ function checkFilterRepel(entity, context, filter1, filter2) {
return true; return true;
} }
// 再判断两者同时成立时取得的行数是否为0 // 再判断两者同时成立时取得的行数是否为0
var filter3 = combineFilters([filter2, filter1]); if (dataCompare) {
var count = context.count(entity, { var filter3 = combineFilters([filter2, filter1]);
filter: filter3, var count = context.count(entity, {
}, { filter: filter3,
dontCollect: true, }, {
blockTrigger: true, dontCollect: true,
}); blockTrigger: true,
if (count instanceof Promise) { });
return count.then(function (count2) { return count2 === 0; }); if (count instanceof Promise) {
return count.then(function (count2) { return count2 === 0; });
}
return count === 0;
} }
return count === 0; return false;
} }
exports.checkFilterRepel = checkFilterRepel; exports.checkFilterRepel = checkFilterRepel;

View File

@ -58,14 +58,6 @@ function abandonModis(filter, context, option) {
_d.action = 'abandon', _d.action = 'abandon',
_d.data = {}, _d.data = {},
_d.filter = filter, _d.filter = filter,
_d.sorter = [
{
$attr: {
$$createAt$$: 1,
},
$direction: 'asc',
}
],
_d), Object.assign({}, option, { _d), Object.assign({}, option, {
blockTrigger: false, blockTrigger: false,
})]))]; })]))];

View File

@ -10,4 +10,4 @@ import { StorageSchema } from "../types/Storage";
*/ */
export declare function judgeRelation<ED extends { export declare function judgeRelation<ED extends {
[E: string]: EntityDef; [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;

View File

@ -59,7 +59,7 @@ function judgeRelation(schema, entity, attr) {
return 2; return 2;
} }
else { 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; return 1;
} }
} }

View File

@ -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 Action = string;
export declare type State = string; export declare type State = string;
export declare type ActionDef<A extends Action, S extends State> = { 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>; [A in keyof E[T]['OpSchema']]?: ActionDef<string, string>;
}; };
}; };
export declare type CascadeActionAuth<A extends Action = ''> = {
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
};

52
lib/types/Auth.d.ts vendored
View File

@ -1,8 +1,8 @@
import { CascadeActionAuth, CascadeRelationAuth } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
import { RefOrExpression } from "./Expression"; export declare type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation';
export declare type CheckerType = 'relation' | 'row' | 'data' | 'expression' | 'expressionRelation';
/** /**
* conditionalFilter是指该action发生时operation所操作的行中有满足conditionalFilter的行 * conditionalFilter是指该action发生时operation所操作的行中有满足conditionalFilter的行
* trigger的filter条件trigger中的说明 * trigger的filter条件trigger中的说明
@ -12,55 +12,55 @@ export declare type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt e
type: 'data'; type: 'data';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>; action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void; 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']); conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise<ED[T]['Selection']['filter']>);
}; };
export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = { export declare type RowChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
priority?: number; priority?: number;
type: 'row'; type: 'row';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>; action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter']); 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; errMsg?: string;
inconsistentRows?: { inconsistentRows?: {
entity: keyof ED; entity: keyof ED;
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection']; selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
}; };
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); 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>> = { export declare type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
priority?: number; priority?: number;
type: 'relation'; type: 'relation';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>; when?: 'after';
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter']; 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; 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; priority?: number;
type: 'expression'; type: 'logical';
when?: 'after';
entity: T; entity: T;
action: ED[T]['Action'] | Array<ED[T]['Action']>; 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) => { checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
entity: T2;
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
filter: ED[T2]['Selection']['filter'];
} | undefined | string;
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']);
}; };
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; priority?: number;
type: 'expressionRelation'; type: 'logicalRelation';
when?: 'after';
entity: T; entity: T;
action: ED[T]['Action'] | Array<ED[T]['Action']>; 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) => { checker: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => void | Promise<void>;
entity: T2;
expr: RefOrExpression<keyof ED[T2]['OpSchema']>;
filter: ED[T2]['Selection']['filter'];
} | undefined | string;
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']);
}; };
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>;
};

10
lib/types/Endpoint.d.ts vendored Normal file
View File

@ -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>;
}

3
lib/types/Endpoint.js Normal file
View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
;

122
lib/types/Entity.d.ts vendored
View File

@ -1,6 +1,3 @@
import { GenericAction } from '../actions/action';
import { ExprOp, MakeFilter, NodeId } from './Demand';
import { OneOf } from './Polyfill';
import { PrimaryKey, Sequence } from './DataType'; import { PrimaryKey, Sequence } from './DataType';
declare type TriggerDataAttributeType = '$$triggerData$$'; declare type TriggerDataAttributeType = '$$triggerData$$';
declare type TriggerTimestampAttributeType = '$$triggerTimestamp$$'; declare type TriggerTimestampAttributeType = '$$triggerTimestamp$$';
@ -18,7 +15,7 @@ export declare const DeleteAtAttribute = "$$deleteAt$$";
export declare const SeqAttribute = "$$seq$$"; export declare const SeqAttribute = "$$seq$$";
export declare type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType | DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType; export declare type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType | DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType;
export declare const initinctiveAttributes: string[]; 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; filter?: A extends 'create' ? undefined : F;
indexFrom?: A extends 'create' ? undefined : number; indexFrom?: A extends 'create' ? undefined : number;
count?: 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<{ export declare type FormUpdateData<SH extends GeneralEntityShape> = Partial<{
[K in keyof Omit<SH, InstinctiveAttributes>]: SH[K] | null; [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; 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; id?: string;
action: A; action: A;
data: DATA; data: D;
sorter?: SORTER; sorter?: S;
} & Filter<A, FILTER>; } & FilterPart<A, F>;
export declare type Selection<DATA extends Object, FILTER extends Object | undefined = undefined, SORT extends Object | undefined = undefined> = Operation<'select', DATA, FILTER, SORT>;
export interface EntityShape { export interface EntityShape {
id: PrimaryKey; id: PrimaryKey;
$$seq$$: Sequence; $$seq$$: Sequence;
@ -61,8 +57,6 @@ export interface EntityShape {
$$updateAt$$: number | Date; $$updateAt$$: number | Date;
$$deleteAt$$?: number | Date | null; $$deleteAt$$?: number | Date | null;
} }
export interface FileCarrierEntityShape extends EntityShape {
}
interface GeneralEntityShape extends EntityShape { interface GeneralEntityShape extends EntityShape {
[K: string]: any; [K: string]: any;
} }
@ -72,28 +66,24 @@ export interface EntityDef {
OpSchema: GeneralEntityShape; OpSchema: GeneralEntityShape;
Action: string; Action: string;
ParticularAction?: string; ParticularAction?: string;
Selection: Omit<DeduceSelection<this['Schema']>, 'action'>; Selection: Omit<Operation<'select', Projection, Filter, Sorter>, 'action'>;
Aggregation: Omit<DeduceAggregation<this['Schema'], DeduceProjection<this['Schema']>, DeduceFilter<this['Schema']>, DeduceSorter<this['Schema']>>, 'action'>; Aggregation: Omit<DeduceAggregation<Projection, Filter, Sorter>, 'action'>;
Operation: DeduceOperation<this['Schema']>; Operation: CUDOperation;
Create: DeduceCreateOperation<this['Schema']>; Create: CreateOperation;
CreateSingle: DeduceCreateSingleOperation<this['Schema']>; CreateSingle: CreateSingleOperation;
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>; CreateMulti: CreateMultipleOperation;
Update: DeduceUpdateOperation<this['Schema']>; Update: UpdateOperation;
Remove: DeduceRemoveOperation<this['Schema']>; Remove: RemoveOperation;
Relation?: string;
} }
export interface EntityDict { export interface EntityDict {
[E: string]: EntityDef; [E: string]: EntityDef;
} }
export interface OtmSubProjection extends Omit<DeduceSelection<any>, 'action'> { export interface OtmSubProjection extends Omit<Operation<'select', any, any, any>, 'action'> {
$entity: string; $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 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; [A in AggregationOp]?: P;
} & { } & {
'#aggr'?: P; '#aggr'?: P;
@ -104,36 +94,40 @@ export declare type AggregationResult<SH extends GeneralEntityShape> = Array<{
'#data'?: Partial<SH>; '#data'?: Partial<SH>;
}>; }>;
export declare type AttrFilter<SH extends GeneralEntityShape> = { 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>>; declare type SortAttr = {
export declare type DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{ [K: string]: any;
[K: string]: number | object | undefined; };
} & ExprOp<keyof SH>>; declare type SorterItem = {
export declare type DeduceSorterItem<SH extends GeneralEntityShape> = { $attr: SortAttr;
$attr: DeduceSorterAttr<SH>;
$direction?: "asc" | "desc"; $direction?: "asc" | "desc";
}; };
export declare type DeduceSorter<SH extends GeneralEntityShape> = Array<DeduceSorterItem<SH>>; declare type Sorter = Array<SorterItem>;
export declare type DeduceSelection<SH extends GeneralEntityShape> = Selection<DeduceProjection<SH>, DeduceFilter<SH>, DeduceSorter<SH>>; declare type Filter = {
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'>; [K: string]: any;
export declare type DeduceCreateOperationData<SH extends GeneralEntityShape> = { };
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; id: string;
} & { [K: string]: any;
[k in keyof Omit<SH, InstinctiveAttributes>]?: any;
}; };
export declare type DeduceCreateSingleOperation<SH extends GeneralEntityShape> = Operation<'create', DeduceCreateOperationData<SH>>; declare type CreateSingleOperation = Operation<'create', CreateOperationData, undefined, undefined>;
export declare type DeduceCreateMultipleOperation<SH extends GeneralEntityShape> = Operation<'create', Array<DeduceCreateOperationData<SH>>>; declare type CreateMultipleOperation = Operation<'create', Array<CreateOperationData>, undefined, undefined>;
export declare type DeduceCreateOperation<SH extends GeneralEntityShape> = DeduceCreateSingleOperation<SH> | DeduceCreateMultipleOperation<SH>; declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
export declare type DeduceUpdateOperationData<SH extends GeneralEntityShape> = { declare type UpdateOperationData = {
[k in keyof Omit<SH, InstinctiveAttributes>]?: any; id?: never;
[k: string]: any;
}; };
export declare type DeduceUpdateOperation<SH extends GeneralEntityShape> = Operation<'update' | string, DeduceUpdateOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>; export declare type UpdateOperation = Operation<string, UpdateOperationData, Filter, Sorter>;
export declare type DeduceRemoveOperationData<SH extends GeneralEntityShape> = { declare type RemoveOperationData = {
[A in keyof Omit<SH, InstinctiveAttributes>]?: any; [k: string]: any;
}; };
export declare type DeduceRemoveOperation<SH extends GeneralEntityShape> = Operation<'remove', DeduceRemoveOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>; export declare type RemoveOperation = Operation<'remove', RemoveOperationData, Filter, Sorter>;
export declare type DeduceOperation<SH extends GeneralEntityShape> = DeduceCreateOperation<SH> | DeduceUpdateOperation<SH> | DeduceRemoveOperation<SH>; export declare type CUDOperation = CreateOperation | UpdateOperation | RemoveOperation;
export declare type CreateOpResult<ED extends EntityDict, T extends keyof ED> = { export declare type CreateOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'c'; a: 'c';
e: T; 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> = { export declare type UpdateOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'u'; a: 'u';
e: T; e: T;
d: DeduceUpdateOperationData<ED[T]['Schema']>; d: UpdateOperationData;
f?: DeduceFilter<ED[T]['Schema']>; f?: Filter;
}; };
export declare type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = { export declare type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'r'; a: 'r';
e: T; e: T;
f?: DeduceFilter<ED[T]['Schema']>; f?: Filter;
}; };
export declare type RelationHierarchy<R extends string> = { export declare type RelationHierarchy<R extends string> = {
[K in R]?: R[]; [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> = { export declare type SelectOpResult<ED extends EntityDict> = {
a: 's'; a: 's';
d: { d: {
@ -172,19 +173,4 @@ export declare type Configuration = {
actionType?: ActionType; actionType?: ActionType;
static?: boolean; 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 {}; export {};

View File

@ -12,4 +12,3 @@ exports.initinctiveAttributes = [exports.PrimaryKeyAttribute, exports.TriggerDat
; ;
; ;
; ;
;

View File

@ -5,6 +5,11 @@ export declare class OakException extends Error {
} }
export declare class OakDataException extends OakException { 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 OakOperExistedException extends OakDataException {
} }
export declare class OakRowUnexistedException extends OakDataException { export declare class OakRowUnexistedException extends OakDataException {

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.makeException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.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 tslib_1 = require("tslib");
var OakException = /** @class */ (function (_super) { var OakException = /** @class */ (function (_super) {
tslib_1.__extends(OakException, _super); tslib_1.__extends(OakException, _super);
@ -36,6 +36,18 @@ var OakDataException = /** @class */ (function (_super) {
return OakDataException; return OakDataException;
}(OakException)); }(OakException));
exports.OakDataException = OakDataException; 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) { var OakOperExistedException = /** @class */ (function (_super) {
tslib_1.__extends(OakOperExistedException, _super); tslib_1.__extends(OakOperExistedException, _super);
function OakOperExistedException() { function OakOperExistedException() {
@ -245,6 +257,9 @@ function makeException(data) {
case 'OakDeadlock': { case 'OakDeadlock': {
return new OakDeadlock(data.message); return new OakDeadlock(data.message);
} }
case 'OakImportDataParseException': {
return new OakImportDataParseException(data.message, data.line, data.header);
}
default: default:
return; return;
} }

View File

@ -4,7 +4,7 @@ export declare type RefOrExpression<A> = RefAttr<A> | Expression<A>;
declare type MathType<A> = RefOrExpression<A> | number; declare type MathType<A> = RefOrExpression<A> | number;
declare type StringType<A> = RefOrExpression<A> | string; declare type StringType<A> = RefOrExpression<A> | string;
interface Add<A> { interface Add<A> {
$add: (MathType<A> | StringType<A>)[]; $add: (MathType<A>)[];
} }
interface Subtract<A> { interface Subtract<A> {
$subtract: [MathType<A>, MathType<A>]; $subtract: [MathType<A>, MathType<A>];
@ -111,6 +111,10 @@ interface DateFloor<A> {
$dateFloor: [RefOrExpression<A> | Date | number, 'y' | 'M' | 'd' | 'h' | 'm' | 's']; $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>; 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> { interface GeoContains<A> {
$contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo]; $contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
} }
@ -118,7 +122,23 @@ interface GeoDistance<A> {
$distance: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo]; $distance: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
} }
declare type GeoExpression<A> = GeoContains<A> | GeoDistance<A>; 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 type ExpressionConstant = Geo | number | Date | string | boolean;
export declare function isGeoExpression<A>(expression: any): expression is GeoExpression<A>; export declare function isGeoExpression<A>(expression: any): expression is GeoExpression<A>;
export declare function isDateExpression<A>(expression: any): expression is DateExpression<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 isBoolExpression<A>(expression: any): expression is BoolExpression<A>;
export declare function isCompareExpression<A>(expression: any): expression is CompareExpression<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 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 isExpression<A>(expression: any): expression is Expression<A>;
export declare function opMultipleParams(op: string): boolean; export declare function opMultipleParams(op: string): boolean;
export declare function execOp(op: string, params: any, obscure?: boolean): ExpressionConstant; export declare function execOp(op: string, params: any, obscure?: boolean): ExpressionConstant;

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); 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 tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert")); var assert_1 = tslib_1.__importDefault(require("assert"));
var dayjs_1 = tslib_1.__importDefault(require("dayjs")); var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
@ -42,6 +42,7 @@ dayjs_1.default.extend(dayOfYear_1.default);
; ;
; ;
; ;
;
function isGeoExpression(expression) { function isGeoExpression(expression) {
if (Object.keys(expression).length == 1) { if (Object.keys(expression).length == 1) {
var op = Object.keys(expression)[0]; var op = Object.keys(expression)[0];
@ -105,13 +106,34 @@ function isMathExpression(expression) {
return false; return false;
} }
exports.isMathExpression = isMathExpression; 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) { function isExpression(expression) {
return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$'); return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$');
} }
exports.isExpression = isExpression; exports.isExpression = isExpression;
function opMultipleParams(op) { function opMultipleParams(op) {
return !['$year', '$month', '$weekday', '$weekOfYear', '$day', '$dayOfMonth', 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; exports.opMultipleParams = opMultipleParams;
function execOp(op, params, obscure) { function execOp(op, params, obscure) {
@ -341,6 +363,9 @@ function execOp(op, params, obscure) {
case '$contains': { case '$contains': {
throw new Error('$contains类型未实现'); throw new Error('$contains类型未实现');
} }
case '$concat': {
return params.join('');
}
default: { default: {
(0, assert_1.default)(false, "\u4E0D\u80FD\u8BC6\u522B\u7684expression\u8FD0\u7B97\u7B26\uFF1A".concat(op)); (0, assert_1.default)(false, "\u4E0D\u80FD\u8BC6\u522B\u7684expression\u8FD0\u7B97\u7B26\uFF1A".concat(op));
} }

17
lib/types/Port.d.ts vendored Normal file
View File

@ -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']>;
};

2
lib/types/Port.js Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -1,5 +1,5 @@
import { ActionType } from '.'; import { ActionType } from '.';
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy } from './Entity'; import { EntityDict, EntityShape, InstinctiveAttributes } from './Entity';
import { DataType, DataTypeParams } from './schema/DataTypes'; import { DataType, DataTypeParams } from './schema/DataTypes';
export declare type Ref = 'ref'; export declare type Ref = 'ref';
export interface Column<SH extends EntityShape> { export interface Column<SH extends EntityShape> {
@ -36,7 +36,7 @@ export declare type UniqConstraint<SH extends EntityShape> = {
attributes: Array<keyof SH>; attributes: Array<keyof SH>;
type?: string; type?: string;
}; };
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> { export interface StorageDesc<SH extends EntityShape> {
storageName?: string; storageName?: string;
comment?: string; comment?: string;
attributes: Attributes<SH>; attributes: Attributes<SH>;
@ -48,9 +48,9 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
static?: true; static?: true;
actions: string[]; actions: string[];
actionType: ActionType; actionType: ActionType;
relationHierarchy?: RelationHierarchy<Relation>; relation?: string[];
view?: true; view?: true;
} }
export declare type StorageSchema<ED extends EntityDict> = { export declare type StorageSchema<ED extends EntityDict> = {
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>; [K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
}; };

View File

@ -37,7 +37,7 @@ export interface UpdateTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
fn: (event: { fn: (event: {
operation: ED[T]['Update']; operation: ED[T]['Update'];
}, context: Cxt, option: OperateOption) => Promise<number> | number; }, 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> { export interface UpdateTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends UpdateTriggerBase<ED, T, Cxt> {
when: 'before' | 'after'; when: 'before' | 'after';
@ -58,7 +58,7 @@ export interface RemoveTriggerBase<ED extends EntityDict, T extends keyof ED, Cx
fn: (event: { fn: (event: {
operation: ED[T]['Remove']; operation: ED[T]['Remove'];
}, context: Cxt, option: OperateOption) => Promise<number> | number; }, 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> { export interface RemoveTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt> {
when: 'before' | 'after'; when: 'before' | 'after';

View File

@ -17,3 +17,5 @@ export * from './Watcher';
export * from './AppLoader'; export * from './AppLoader';
export * from './Connector'; export * from './Connector';
export * from './Timer'; export * from './Timer';
export * from './Port';
export * from './Endpoint';

View File

@ -20,3 +20,5 @@ tslib_1.__exportStar(require("./Watcher"), exports);
tslib_1.__exportStar(require("./AppLoader"), exports); tslib_1.__exportStar(require("./AppLoader"), exports);
tslib_1.__exportStar(require("./Connector"), exports); tslib_1.__exportStar(require("./Connector"), exports);
tslib_1.__exportStar(require("./Timer"), exports); tslib_1.__exportStar(require("./Timer"), exports);
tslib_1.__exportStar(require("./Port"), exports);
tslib_1.__exportStar(require("./Endpoint"), exports);

1
lib/utils/date.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export declare function excelStringToDate(str: string | number): number | undefined;

18
lib/utils/date.js Normal file
View File

@ -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;

View File

@ -1,6 +1,6 @@
{ {
"name": "oak-domain", "name": "oak-domain",
"version": "2.3.2", "version": "2.4.0",
"author": { "author": {
"name": "XuChang" "name": "XuChang"
}, },
@ -37,7 +37,7 @@
"mocha": "^8.2.1", "mocha": "^8.2.1",
"ts-node": "~10.9.1", "ts-node": "~10.9.1",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"typescript": "~4.7.4" "typescript": "^4.9.4"
}, },
"dependencies": { "dependencies": {
"@datasert/cronjs-matcher": "^1.2.0", "@datasert/cronjs-matcher": "^1.2.0",

View File

@ -1 +1,30 @@
export type GenericRelation = 'owner'; 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;
}

View File

@ -1,13 +1,15 @@
import { EntityDict } from '../base-app-domain'; import { EntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore'; import { AsyncContext } from '../store/AsyncRowStore';
import { createRelationHierarchyCheckers } from '../store/checker'; import { createAuthCheckers } from '../store/checker';
import { createModiRelatedCheckers } from '../store/modi'; import { createModiRelatedCheckers } from '../store/modi';
import { SyncContext } from '../store/SyncRowStore'; import { SyncContext } from '../store/SyncRowStore';
import { StorageSchema, EntityDict as BaseEntityDict, Checker } 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>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema)); checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
checkers.push(...createRelationHierarchyCheckers<ED, Cxt>(schema)); if (authDict) {
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
}
return checkers; return checkers;
} }

View File

@ -25,7 +25,8 @@ const Schema: Record<string, {
states: string[]; states: string[];
sourceFile: ts.SourceFile; sourceFile: ts.SourceFile;
locale: ts.ObjectLiteralExpression; locale: ts.ObjectLiteralExpression;
relationHierarchy?: ts.ObjectLiteralExpression; // relationHierarchy?: ts.ObjectLiteralExpression;
// reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
toModi: boolean; toModi: boolean;
actionType: string; actionType: string;
static: boolean; static: boolean;
@ -497,7 +498,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
const localEnumStringTypes: string[] = []; const localEnumStringTypes: string[] = [];
const additionalImports: ts.ImportDeclaration[] = []; const additionalImports: ts.ImportDeclaration[] = [];
let localeDef: ts.ObjectLiteralExpression | undefined = undefined; let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined; // let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
ts.forEachChild(sourceFile!, (node) => { ts.forEachChild(sourceFile!, (node) => {
if (ts.isImportDeclaration(node)) { if (ts.isImportDeclaration(node)) {
const entityImported = getEntityImported(node); const entityImported = getEntityImported(node);
@ -1123,15 +1125,22 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
_static = true; // static如果有值只能为true _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 // RelationHierary
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`); assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
const { initializer } = declaration; const { initializer } = declaration;
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`); assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
relationHierarchy = initializer; 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 { 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, locale: localeDef,
}); });
} }
if (hasRelationDef) { /* if (hasRelationDef) {
assert(relationHierarchy, `${filename}中缺少了relationHierarchy定义`); if(!relationHierarchy && !reverseCascadeRelationHierarchy){
assign(schema, { console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`);
relationHierarchy, }
}); if (relationHierarchy) {
assign(schema, {
relationHierarchy,
});
}
if (reverseCascadeRelationHierarchy) {
assign(schema, {
reverseCascadeRelationHierarchy,
});
}
} }
else { else {
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`); assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
} assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
} */
assign(Schema, { assign(Schema, {
[moduleName]: schema, [moduleName]: schema,
@ -2864,10 +2883,6 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("DeduceAggregation"), factory.createIdentifier("DeduceAggregation"),
[ [
factory.createTypeReferenceNode(
factory.createIdentifier("Schema"),
undefined
),
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("Projection"), factory.createIdentifier("Projection"),
undefined undefined
@ -3078,6 +3093,13 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
const reverseOneNodes: ts.TypeNode[] = []; const reverseOneNodes: ts.TypeNode[] = [];
if (ReversePointerEntities[entity]) { if (ReversePointerEntities[entity]) {
if (ReversePointerRelations[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]) { for (const one of ReversePointerRelations[entity]) {
const cascadeCreateNode = factory.createTypeLiteralNode( const cascadeCreateNode = factory.createTypeLiteralNode(
[ [
@ -3136,14 +3158,14 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
factory.createPropertySignature( factory.createPropertySignature(
undefined, undefined,
factory.createIdentifier('entity'), factory.createIdentifier('entity'),
undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc questionToken,
factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`) factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`)
) )
), ),
factory.createPropertySignature( factory.createPropertySignature(
undefined, undefined,
factory.createIdentifier('entityId'), factory.createIdentifier('entityId'),
undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc questionToken,
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("String"), factory.createIdentifier("String"),
[factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))]
@ -4292,10 +4314,6 @@ function constructActions(statements: Array<ts.Statement>, entity: string) {
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("RemoveOperation"), factory.createIdentifier("RemoveOperation"),
undefined undefined
),
factory.createTypeReferenceNode(
factory.createIdentifier("SelectOperation"),
undefined
) )
]) ])
) )
@ -4536,23 +4554,6 @@ const initialStatements = () => [
function outputSubQuery(outputDir: string, printer: ts.Printer) { function outputSubQuery(outputDir: string, printer: ts.Printer) {
const statements: ts.Statement[] = []; const statements: ts.Statement[] = [];
if (process.env.COMPLING_AS_LIB) { 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) { for (const entity in Schema) {
// import * as User from '../User/Schema'; // import * as User from '../User/Schema';
@ -4903,7 +4904,8 @@ function outputSchema(outputDir: string, printer: ts.Printer) {
constructSorter(statements, entity); constructSorter(statements, entity);
constructActions(statements, entity); constructActions(statements, entity);
constructQuery(statements, entity); constructQuery(statements, entity);
constructFullAttrs(statements, entity); // 现在FullAttrs和NativeAttrs似乎没什么用还会引起递归
// constructFullAttrs(statements, entity);
const makeActionArguments: ts.TypeNode[] = []; const makeActionArguments: ts.TypeNode[] = [];
if (ActionAsts[entity]) { 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( statements.push(
factory.createTypeAliasDeclaration( factory.createTypeAliasDeclaration(
undefined, undefined,
@ -5638,7 +5653,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
for (const entity in Schema) { for (const entity in Schema) {
const indexExpressions: ts.Expression[] = []; 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 = [ const fromSchemaSpecifiers = [
factory.createImportSpecifier( factory.createImportSpecifier(
false, false,
@ -5646,7 +5661,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
factory.createIdentifier("OpSchema") factory.createIdentifier("OpSchema")
) )
]; ];
if (relationHierarchy) { /* if (relationHierarchy || reverseCascadeRelationHierarchy) {
fromSchemaSpecifiers.push( fromSchemaSpecifiers.push(
factory.createImportSpecifier( factory.createImportSpecifier(
false, false,
@ -5654,7 +5669,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
factory.createIdentifier("Relation") factory.createIdentifier("Relation")
) )
); );
} } */
const statements: ts.Statement[] = [ const statements: ts.Statement[] = [
factory.createImportDeclaration( factory.createImportDeclaration(
undefined, undefined,
@ -5855,7 +5870,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
) )
); );
} }
if (relationHierarchy) { /* if (relationHierarchy) {
propertyAssignments.push( propertyAssignments.push(
factory.createPropertyAssignment( factory.createPropertyAssignment(
factory.createIdentifier("relationHierarchy"), 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 = [ const sdTypeArguments = [
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("OpSchema"), factory.createIdentifier("OpSchema"),
undefined undefined
) )
]; ];
if (relationHierarchy) {
sdTypeArguments.push(
factory.createTypeReferenceNode(
factory.createIdentifier("Relation"),
undefined
)
)
}
statements.push( statements.push(
factory.createVariableStatement( factory.createVariableStatement(
[factory.createModifier(ts.SyntaxKind.ExportKeyword)], [factory.createModifier(ts.SyntaxKind.ExportKeyword)],

View File

@ -1,7 +1,7 @@
import assert from "assert"; import assert from "assert";
import { import {
EntityDict, EntityDict,
OperateOption, SelectOption, OperationResult, DeduceFilter, CreateAtAttribute, UpdateAtAttribute, AggregationResult, DeleteAtAttribute OperateOption, SelectOption, OperationResult, CreateAtAttribute, UpdateAtAttribute, AggregationResult, DeleteAtAttribute
} from "../types/Entity"; } from "../types/Entity";
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { RowStore } from '../types/RowStore'; import { RowStore } from '../types/RowStore';
@ -525,7 +525,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
operation: ED[T2]['Operation'], operation: ED[T2]['Operation'],
context: Cxt, context: Cxt,
option: OP) => R, option: OP) => R,
filter?: DeduceFilter<ED[T]['Schema']> filter?: ED[T]['Update']['filter']
) { ) {
const modiAttr = this.getSchema()[entity].toModi; const modiAttr = this.getSchema()[entity].toModi;
const option2 = Object.assign({}, option); const option2 = Object.assign({}, option);
@ -830,7 +830,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
} }
// 对更新的数据去掉所有的undefined属性 // 对更新的数据去掉所有的undefined属性
protected preProcessDataUpdated<T extends keyof ED>(data: ED[T]['Update']['data']) { protected preProcessDataUpdated(data: Record<string, any>) {
const undefinedKeys = Object.keys(data).filter( const undefinedKeys = Object.keys(data).filter(
ele => data[ele] === undefined ele => data[ele] === undefined
); );
@ -852,7 +852,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
* @param option * @param option
*/ */
private async doUpdateSingleRowAsync<T extends keyof ED, OP extends OperateOption, Cxt extends AsyncContext<ED>>(entity: T, 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, context: Cxt,
option: OP option: OP
) { ) {
@ -1182,7 +1182,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
id: { id: {
$in: ids, $in: ids,
} }
} as DeduceFilter<ED[T]['Schema']>, },
}); });
} }
} }
@ -1202,7 +1202,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
id: { id: {
$in: ids, $in: ids,
} }
} as DeduceFilter<ED[T]['Schema']>, },
}); });
} }
} }

View File

@ -1,10 +1,10 @@
import assert from 'assert'; import assert from 'assert';
import { pull, unset } from "../utils/lodash"; import { pull, unset } from "../utils/lodash";
import { addFilterSegment, checkFilterRepel } from "../store/filter"; 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 { EntityDict as BaseEntityDict } from '../base-app-domain';
import { Logger } from "../types/Logger"; 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 { Trigger, CreateTriggerCrossTxn, CreateTrigger, CreateTriggerInTxn, SelectTriggerAfter, UpdateTrigger } from "../types/Trigger";
import { AsyncContext } from './AsyncRowStore'; import { AsyncContext } from './AsyncRowStore';
import { SyncContext } from './SyncRowStore'; 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 { registerChecker<T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): void {
const { entity, action, type, conditionalFilter } = checker; const { entity, action, type, conditionalFilter } = checker;
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`; const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
const fn = translateCheckerInAsyncContext(checker); const { fn, when } = translateCheckerInAsyncContext(checker);
const trigger = { const trigger = {
checkerType: type, checkerType: type,
name: triggerName, name: triggerName,
priority: checker.priority || 2, // checker的默认优先级稍高一点点 priority: checker.priority || 20, // checker的默认优先级稍高
entity, entity,
action: action as 'update', action: action as 'update',
fn, fn,
when: 'before', when,
filter: conditionalFilter, filter: conditionalFilter,
} as UpdateTrigger<ED, T, Cxt>; } as UpdateTrigger<ED, T, Cxt>;
this.registerTrigger(trigger); this.registerTrigger(trigger);
@ -77,7 +77,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
throw new Error(`不可有同名的触发器「${trigger.name}`); throw new Error(`不可有同名的触发器「${trigger.name}`);
} }
if (typeof trigger.priority !== 'number') { if (typeof trigger.priority !== 'number') {
trigger.priority = 1; // 默认最低 trigger.priority = 10; // 默认值
} }
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) { if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
assert(typeof trigger.action === 'string' && trigger.action !== 'create' assert(typeof trigger.action === 'string' && trigger.action !== 'create'
@ -229,8 +229,9 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
// trigger只对满足条件的前项进行判断如果确定不满足可以pass // trigger只对满足条件的前项进行判断如果确定不满足可以pass
assert(operation.action !== 'create'); assert(operation.action !== 'create');
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>; const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter; const filterr = typeof filter === 'function' ? filter(operation as ED[T]['Update'], context, option) : filter;
const filterRepelled = checkFilterRepel(entity, context, filterr, operation.filter) as boolean assert(!(filterr instanceof Promise));
const filterRepelled = checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as boolean
if (filterRepelled) { if (filterRepelled) {
continue; continue;
} }
@ -252,8 +253,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) { if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
assert(operation.action !== 'create'); assert(operation.action !== 'create');
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>; const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter; const filterr = typeof filter === 'function' ? await filter(operation as ED[T]['Update'], context, option) : filter;
const filterRepelled = await (checkFilterRepel(entity, context, filterr, operation.filter) as Promise<boolean>); const filterRepelled = await (checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as Promise<boolean>);
if (filterRepelled) { if (filterRepelled) {
return execPreTrigger(idx + 1); return execPreTrigger(idx + 1);
} }
@ -272,8 +273,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) { if ((trigger as UpdateTrigger<ED, T, Cxt>).filter) {
assert(operation.action !== 'create'); assert(operation.action !== 'create');
const { filter } = trigger as UpdateTrigger<ED, T, Cxt>; const { filter } = trigger as UpdateTrigger<ED, T, Cxt>;
const filterr = typeof filter === 'function' ? filter(operation, context, option) : filter; const filterr = typeof filter === 'function' ? await filter(operation as ED[T]['Update'], context, option) : filter;
const filterRepelled = await (checkFilterRepel(entity, context, filterr, operation.filter) as Promise<boolean>); const filterRepelled = await (checkFilterRepel<ED, T, Cxt>(entity, context, filterr, operation.filter) as Promise<boolean>);
if (filterRepelled) { if (filterRepelled) {
return execCommitTrigger(idx + 1); return execCommitTrigger(idx + 1);
} }
@ -294,7 +295,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
return async () => { return async () => {
await context.begin(); await context.begin();
const number = await (trigger as CreateTrigger<ED, T, AsyncContext<ED>>).fn({ 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); }, context, option);
if ((trigger as CreateTriggerCrossTxn<ED, T, Cxt>).strict === 'makeSure') { if ((trigger as CreateTriggerCrossTxn<ED, T, Cxt>).strict === 'makeSure') {
// 如果是必须完成的trigger在完成成功后要把trigger相关的属性置null; // 如果是必须完成的trigger在完成成功后要把trigger相关的属性置null;
@ -354,10 +355,10 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
); );
if (triggers) { if (triggers) {
const postTriggers = triggers.filter( 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( 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) { if (context instanceof SyncContext) {

View File

@ -1,38 +1,51 @@
import assert from 'assert'; import assert from 'assert';
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter"; import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
import { OakDataException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception'; import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import { Checker, CreateTriggerInTxn, EntityDict, ExpressionRelationChecker, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn } from "../types"; import {
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy
} from "../types";
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from "./AsyncRowStore"; import { AsyncContext } from "./AsyncRowStore";
import { getFullProjection } from './actionDef'; import { getFullProjection } from './actionDef';
import { SyncContext } from './SyncRowStore'; import { SyncContext } from './SyncRowStore';
import { firstLetterUpperCase } from '../utils/string'; import { firstLetterUpperCase } from '../utils/string';
import { uniq } from '../utils/lodash';
import { judgeRelation } from './relation';
export function translateCheckerInAsyncContext< export function translateCheckerInAsyncContext<
ED extends EntityDict & BaseEntityDict, ED extends EntityDict & BaseEntityDict,
Cxt extends AsyncContext<ED> Cxt extends AsyncContext<ED>
>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn'] { >(checker: Checker<ED, keyof ED, Cxt>): {
const { entity, type } = checker; 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) { switch (type) {
case 'data': { case 'data': {
const { checker: checkerFn } = checker; const { checker: checkerFn } = checker;
return (async ({ operation }, context) => { const fn = (async ({ operation }, context) => {
const { data } = operation; const { data } = operation;
checkerFn(data, context); await checkerFn(data, context);
return 0; return 0;
}) as CreateTriggerInTxn<ED, keyof ED, Cxt>['fn']; }) as CreateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
return {
fn,
when,
};
} }
case 'row': { case 'row': {
const { filter, errMsg, inconsistentRows } = checker; const { filter, errMsg, inconsistentRows } = checker;
return (async ({ operation }, context, option) => { const fn = (async ({ operation }, context, option) => {
const { filter: operationFilter, action } = operation; 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)) { if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(operationFilter || {}, filter2); operation.filter = addFilterSegment(operationFilter || {}, filter2);
return 0; return 0;
} }
else { else {
if (await checkFilterContains(entity, context, filter2, operationFilter || {})) { if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, operationFilter || {}, true)) {
return 0; return 0;
} }
if (inconsistentRows) { if (inconsistentRows) {
@ -81,47 +94,61 @@ export function translateCheckerInAsyncContext<
} }
} }
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn']; }) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
return {
fn,
when,
};
} }
case 'relation': { case 'relation': {
const { relationFilter } = checker; const { relationFilter, errMsg } = checker;
return (async ({ operation }, context, option) => { const fn = (async ({ operation }, context, option) => {
if (context.isRoot()) { if (context.isRoot()) {
return 0; return 0;
} }
// assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
// 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤) // 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
operation.filter = combineFilters([operation.filter, relationFilter(operation, context, option)]); if (operation.action === 'create') {
return 0; const filter2 = await relationFilter(operation, context, option);
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn'];
} const { data } = operation as ED[keyof ED]['Create'];
case 'expression': const filter = data instanceof Array ? {
case 'expressionRelation': { id: {
const { expression, errMsg } = checker; $in: data.map(
return (async ({ operation }, context, option) => { ele => ele.id,
if (context.isRoot() && type === 'expressionRelation') { ),
return 0; },
} } : {
const exprResult = expression(operation, context, option); id: data.id,
if (typeof exprResult === 'string') { };
throw new OakUserUnpermittedException(exprResult || errMsg); if (await checkFilterContains<ED, keyof ED, Cxt>(entity, context, filter2, filter, true)) {
} return 0;
else if (exprResult === undefined) { }
return 0; throw new OakUserUnpermittedException(errMsg);
} }
else { else {
const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult; operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
const [result] = await context.select(expressionEntity, {
data: {
$expr: expr,
},
filter: expressionFilter,
}, Object.assign({}, option, { dontCollect: true }));
if (!result) {
// 条件判定为假,抛异常
throw new OakUserUnpermittedException(errMsg);
}
} }
return 0; return 0;
}) as UpdateTriggerInTxn<ED, keyof ED, Cxt>['fn']; }) 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: { default: {
assert(false); assert(false);
@ -133,74 +160,85 @@ export function translateCheckerInSyncContext<
ED extends EntityDict & BaseEntityDict, ED extends EntityDict & BaseEntityDict,
T extends keyof ED, T extends keyof ED,
Cxt extends SyncContext<ED> Cxt extends SyncContext<ED>
>(checker: Checker<ED, T, Cxt>): (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void { >(checker: Checker<ED, T, Cxt>): {
const { entity, type } = checker; 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) { switch (type) {
case 'data': { case 'data': {
const { checker: checkerFn } = checker; 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': { case 'row': {
const { filter, errMsg } = checker; 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 { 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); assert(operationFilter);
if (['select', 'count', 'stat'].includes(action)) { if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(operationFilter, filter2); operation.filter = addFilterSegment(operationFilter, filter2);
return 0; return 0;
} }
else { 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; return;
} }
throw new OakRowInconsistencyException(undefined, errMsg); throw new OakRowInconsistencyException(undefined, errMsg);
} }
}; };
return {
fn,
when,
};
} }
case 'relation': { case 'relation': {
const { relationFilter: filter, errMsg } = checker; const { relationFilter, errMsg } = checker;
return (operation, context, option) => { const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
if (context.isRoot()) { if (context.isRoot()) {
return; return;
} }
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter; const filter2 = typeof relationFilter === 'function' ? relationFilter(operation, context, option) : relationFilter;
const { filter: operationFilter } = operation; const { filter, action } = operation;
assert(operationFilter); let filter3 = filter;
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, operationFilter)) { 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; return;
} }
throw new OakUserUnpermittedException(errMsg); throw new OakUserUnpermittedException(errMsg);
}; };
} return {
case 'expression': fn,
case 'expressionRelation': { when,
const { expression, errMsg } = checker; };
return (operation, context, option) => { }
if (context.isRoot() && type === 'expressionRelation') { case 'logical':
return; case 'logicalRelation': {
} const { checker: checkerFn } = checker;
const exprResult = expression(operation, context, option); const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
if (typeof exprResult === 'string') { if (context.isRoot() && type === 'logicalRelation') {
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);
}
return; return;
} }
checkerFn(operation, context, option);
};
return {
fn,
when,
}; };
} }
default: { 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>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
for (const entity in schema) { for (const entity in schema) {
const { relationHierarchy } = schema[entity]; if (authDict[entity]) {
if (relationHierarchy) { const { relationAuth, actionAuth } = authDict[entity]!;
// 先build反向hierarchy的map if (relationAuth) {
const reverseHierarchy = {} as Record<string, string[]>; const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
for (const r in relationHierarchy) { for (const r in relationAuth) {
for (const r2 of relationHierarchy[r]!) { Object.assign(raFilterMakerDict, {
if (!reverseHierarchy[r2]) { [r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
reverseHierarchy[r2] = [r]; });
}
else {
reverseHierarchy[r2].push(r);
}
} }
} const userEntityName = `user${firstLetterUpperCase(entity)}`;
const entityIdAttr = `${entity}Id`;
// 对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) {
checkers.push({ checkers.push({
entity: userEntityName as keyof ED, entity: userEntityName as keyof ED,
action: 'remove', action: 'create',
type: 'expressionRelation', type: 'relation',
conditionalFilter: { relationFilter: (operation, context) => {
relation: r, const { data } = operation as ED[keyof ED]['Create'];
} as ED[keyof ED]['Update']['filter'], assert(!(data instanceof Array));
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => { const { relation, [entityIdAttr]: entityId } = data;
const userId = context.getCurrentUserId(); const userId = context.getCurrentUserId();
const { filter } = operation as ED[keyof ED]['Remove']; if (!raFilterMakerDict[relation]) {
const legalRelations = reverseHierarchy[r]; return;
if (legalRelations.length === 0) {
return '这是不应该跑出来的情况,请杀程序员祭天';
} }
const filter = raFilterMakerDict[relation]!(userId!);
return { return {
entity: userEntityName as T2, [entity]: filter,
expr: { };
$gt: [{
'#attr': '$$createAt$$',
}, 0]
},
filter: {
userId,
[entityIdAttr]: {
$in: {
entity: userEntityName,
data: {
[entityIdAttr]: 1,
},
filter,
}
},
relation: {
$in: legalRelations,
}
},
}
}, },
errMsg: '越权操作', 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 等实现的时候再写
} }
/* // if (actionAuth) {
checkers.push({ for (const a in actionAuth) {
entity: userEntityName as keyof ED, const filterMaker = translateActionAuthFilterMaker(schema, actionAuth[a as ED[keyof ED]['Action']]!, entity);
action: 'create' as ED[keyof ED]['Action'], checkers.push({
type: 'data', entity,
checker: (data, context) => { action: a as ED[keyof ED]['Action'],
assert(!(data instanceof Array)); type: 'relation',
const { userId } = data as ED[keyof ED]['CreateSingle']['data']; relationFilter: (operation, context) => {
const userId2 = context.getCurrentUserId(true); // const { filter } = operation;
if (userId === userId2) { const filter = filterMaker(context.getCurrentUserId()!);
throw new OakDataException('不允许授权给自己'); 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 等实现的时候再写
} }
} }

View File

@ -732,14 +732,14 @@ export function getRelevantIds<ED extends EntityDict, T extends keyof ED>(filter
if (filter?.$and) { if (filter?.$and) {
const idss = filter.$and.map( const idss = filter.$and.map(
ele => getRelevantIds(ele) (ele: ED[T]['Selection']['filter']) => getRelevantIds(ele)
); );
idsAnd = intersection(...idss); idsAnd = intersection(...idss);
} }
if (filter?.$or) { if (filter?.$or) {
const idss = filter.$or.map( const idss = filter.$or.map(
ele => getRelevantIds(ele) (ele: ED[T]['Selection']['filter']) => getRelevantIds(ele)
); );
idsOr = union(...idss); idsOr = union(...idss);
} }
@ -879,11 +879,21 @@ export function makeTreeDescendantFilter<ED extends EntityDict, T extends keyof
return currentLevelInFilter; return currentLevelInFilter;
} }
/**
* filter是否包含containedfilter查询的数据一定满足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>>( export function checkFilterContains<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(
entity: T, entity: T,
context: Cxt, context: Cxt,
contained: ED[T]['Selection']['filter'], 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) { if (!filter) {
throw new OakRowInconsistencyException(); throw new OakRowInconsistencyException();
} }
@ -892,29 +902,34 @@ export function checkFilterContains<ED extends EntityDict, T extends keyof ED, C
if (contains(entity, schema, filter, contained)) { if (contains(entity, schema, filter, contained)) {
return true; return true;
} }
// 再判断加上了conditionalFilter后取得的行数是否缩减 if (dataCompare) {
const filter2 = combineFilters([filter, { // 再判断加上了conditionalFilter后取得的行数是否缩减
$not: contained, const filter2 = combineFilters([filter, {
}]); $not: contained,
const count = context.count(entity, { }]);
filter: filter2, const count = context.count(entity, {
}, { filter: filter2,
dontCollect: true, }, {
blockTrigger: true, dontCollect: true,
}); blockTrigger: true,
if (count instanceof Promise) { });
return count.then( if (count instanceof Promise) {
(count2) => count2 === 0 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>>( export function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(
entity: T, entity: T,
context: Cxt, context: Cxt,
filter1: ED[T]['Selection']['filter'], 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) { if (!filter2) {
throw new OakRowInconsistencyException(); throw new OakRowInconsistencyException();
} }
@ -924,17 +939,20 @@ export function checkFilterRepel<ED extends EntityDict, T extends keyof ED, Cxt
return true; return true;
} }
// 再判断两者同时成立时取得的行数是否为0 // 再判断两者同时成立时取得的行数是否为0
const filter3 = combineFilters([filter2, filter1]); if (dataCompare) {
const count = context.count(entity, { const filter3 = combineFilters([filter2, filter1]);
filter: filter3, const count = context.count(entity, {
}, { filter: filter3,
dontCollect: true, }, {
blockTrigger: true, dontCollect: true,
}); blockTrigger: true,
if (count instanceof Promise) { });
return count.then( if (count instanceof Promise) {
(count2) => count2 === 0 return count.then(
); (count2) => count2 === 0
);
}
return count === 0;
} }
return count === 0; return false;
} }

View File

@ -1,6 +1,6 @@
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { OpSchema as Modi, Filter } from '../base-app-domain/Modi/Schema'; 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 { appendOnlyActions } from '../actions/action';
import { difference } from '../utils/lodash'; import { difference } from '../utils/lodash';
import { AsyncContext } from './AsyncRowStore'; import { AsyncContext } from './AsyncRowStore';
@ -52,14 +52,14 @@ export async function abandonModis<ED extends EntityDict & BaseEntityDict, Cxt e
action: 'abandon', action: 'abandon',
data: {}, data: {},
filter, filter,
sorter: [ /* sorter: [
{ {
$attr: { $attr: {
$$createAt$$: 1, $$createAt$$: 1,
}, },
$direction: 'asc', $direction: 'asc',
} }
] ] */
}, Object.assign({}, option, { }, Object.assign({}, option, {
blockTrigger: false, blockTrigger: false,
})); }));

View File

@ -63,7 +63,7 @@ export function judgeRelation<ED extends {
return 2; return 2;
} }
else { else {
assert(initinctiveAttributes.includes(attr), `${attr}属性找不到`); assert(initinctiveAttributes.includes(attr), `${entity as string}对象中的${attr}属性找不到`);
return 1; return 1;
} }
} }

View File

@ -1,7 +1,7 @@
import assert from 'assert'; import assert from 'assert';
import { getAttrRefInExpression, StorageSchema } from '../types'; import { getAttrRefInExpression, StorageSchema } from '../types';
import { EXPRESSION_PREFIX } from '../types/Demand'; import { EXPRESSION_PREFIX } from '../types/Demand';
import { DeduceFilter, EntityDict } from '../types/Entity'; import { EntityDict } from '../types/Entity';
import { getRelevantIds } from './filter'; import { getRelevantIds } from './filter';
import { judgeRelation } from './relation'; import { judgeRelation } from './relation';
@ -29,7 +29,7 @@ export function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<
const toBeAssignNode: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点 const toBeAssignNode: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
// filter当中所关联到的属性必须在projection中 // filter当中所关联到的属性必须在projection中
const filterNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {}; 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']; const necessaryAttrs: string[] = ['id'];
for (const attr in filterNode) { for (const attr in filterNode) {
if (attr === '#id') { if (attr === '#id') {

View File

@ -1,4 +1,5 @@
import { EntityDict } from "./Entity"; import { CascadeRelationItem, EntityDict } from "./Entity";
import { GenericAction } from '../actions/action';
export type Action = string; export type Action = string;
export type State = string; export type State = string;
@ -14,4 +15,9 @@ export type ActionDictOfEntityDict<E extends EntityDict> = {
[T in keyof E]?: { [T in keyof E]?: {
[A in keyof E[T]['OpSchema']]?: ActionDef<string, string>; [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[])[];
};

View File

@ -1,9 +1,10 @@
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
import { RefOrExpression } from "./Expression"; import { RefOrExpression } from "./Expression";
export type CheckerType = 'relation' | 'row' | 'data' | 'expression' | 'expressionRelation'; export type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation';
/** /**
* conditionalFilter是指该action发生时operation所操作的行中有满足conditionalFilter的行 * conditionalFilter是指该action发生时operation所操作的行中有满足conditionalFilter的行
@ -14,8 +15,10 @@ export type DataChecker<ED extends EntityDict, T extends keyof ED, Cxt extends A
type: 'data'; type: 'data';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>; action: Omit<ED[T]['Action'], 'remove'> | Array<Omit<ED[T]['Action'], 'remove'>>;
checker: (data: ED[T]['Create']['data'] | ED[T]['Update']['data'], context: Cxt) => void; 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']); 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>> = { 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'; type: 'row';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>; action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>;
filter: ED[T]['Selection']['filter'] | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter']); // 对行的额外检查条件 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; errMsg?: string;
inconsistentRows?: { // 因为这里的限制不一定在本row上如果不传这个exception则默认返回本row上的exception inconsistentRows?: { // 因为这里的限制不一定在本row上如果不传这个exception则默认返回本row上的exception
entity: keyof ED; entity: keyof ED;
selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection']; selection: (filter?: ED[T]['Selection']['filter']) => ED[keyof ED]['Selection'];
}; };
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); 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>> = { export type RelationChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
priority?: number; priority?: number;
type: 'relation'; type: 'relation';
entity: T; entity: T;
action: Omit<ED[T]['Action'], 'create'> | Array<Omit<ED[T]['Action'], 'create'>>; when?: 'after';
relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => ED[T]['Selection']['filter']; // 生成一个额外的relation相关的filter加在原先的filter上 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; 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']); 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; priority?: number;
type: 'expression'; type: 'logicalRelation';
when?: 'after';
entity: T; entity: T;
action: ED[T]['Action'] | Array<ED[T]['Action']>; 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) => { checker: (
entity: T2; operation: ED[T]['Operation'] | ED[T]['Selection'],
expr: RefOrExpression<keyof ED[T2]['OpSchema']>; context: Cxt,
filter: ED[T2]['Selection']['filter']; option: OperateOption | SelectOption
} | undefined | string; // 生成一个带表达式的查询任务结果为true代表可以过为false不可以。如果返回undefined直接过返回string直接挂 ) => void | Promise<void>;
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;
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
}; };
export type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = 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>;
};

11
src/types/Endpoint.ts Normal file
View File

@ -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>;
};

View File

@ -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'; import { PrimaryKey, Sequence } from './DataType';
type TriggerDataAttributeType = '$$triggerData$$'; type TriggerDataAttributeType = '$$triggerData$$';
@ -22,7 +19,7 @@ export const SeqAttribute = '$$seq$$';
export type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType| DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType; export type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType| DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType;
export const initinctiveAttributes = [PrimaryKeyAttribute, TriggerDataAttribute, TriggerTimestampAttribute, CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute, SeqAttribute]; 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; filter?: A extends 'create' ? undefined : F;
indexFrom?: A extends 'create' ? undefined : number; indexFrom?: A extends 'create' ? undefined : number;
count?: 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; [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, export type Operation<A extends string,
DATA extends Object, D extends Projection,
FILTER extends Object | undefined = undefined, F extends Filter | undefined = undefined,
SORTER extends Object | undefined = undefined> = { S extends Sorter | undefined = undefined> = {
id?: string; // 为了一致性每个operation也应当保证唯一id id?: string; // 为了一致性每个operation也应当保证唯一id
action: A; action: A;
data: DATA; data: D;
sorter?: SORTER; sorter?: S;
} & Filter<A, FILTER>; } & FilterPart<A, F>;
export type Selection<DATA extends Object,
FILTER extends Object | undefined = undefined,
SORT extends Object | undefined = undefined> = Operation<'select', DATA, FILTER, SORT>;
export interface EntityShape { export interface EntityShape {
id: PrimaryKey; id: PrimaryKey;
@ -76,9 +69,6 @@ export interface EntityShape {
$$deleteAt$$?: number | Date | null; $$deleteAt$$?: number | Date | null;
} }
export interface FileCarrierEntityShape extends EntityShape {
};
interface GeneralEntityShape extends EntityShape { interface GeneralEntityShape extends EntityShape {
[K: string]: any; [K: string]: any;
} }
@ -91,33 +81,28 @@ export interface EntityDef {
OpSchema: GeneralEntityShape; OpSchema: GeneralEntityShape;
Action: string; Action: string;
ParticularAction?: string; ParticularAction?: string;
Selection: Omit<DeduceSelection<this['Schema']>, 'action'>; Selection: Omit<Operation<'select', Projection, Filter, Sorter>, 'action'>;
Aggregation: Omit<DeduceAggregation<this['Schema'], DeduceProjection<this['Schema']>, DeduceFilter<this['Schema']>, DeduceSorter<this['Schema']>>, 'action'>; Aggregation: Omit<DeduceAggregation<Projection, Filter, Sorter>, 'action'>;
Operation: DeduceOperation<this['Schema']>; Operation: CUDOperation;
Create: DeduceCreateOperation<this['Schema']>; Create: CreateOperation;
CreateSingle: DeduceCreateSingleOperation<this['Schema']>; CreateSingle: CreateSingleOperation;
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>; CreateMulti: CreateMultipleOperation;
Update: DeduceUpdateOperation<this['Schema']>; Update: UpdateOperation;
Remove: DeduceRemoveOperation<this['Schema']>; Remove: RemoveOperation;
Relation?: string;
}; };
export interface EntityDict { export interface EntityDict {
[E: string]: EntityDef; [E: string]: EntityDef;
}; };
export interface OtmSubProjection extends Omit<DeduceSelection<any>, 'action'> { export interface OtmSubProjection extends Omit<Operation<'select', any, any, any>, 'action'> {
$entity: string; $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 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; [A in AggregationOp]?: P;
} & { } & {
'#aggr'?: P; '#aggr'?: P;
@ -130,57 +115,58 @@ export type AggregationResult<SH extends GeneralEntityShape> = Array<{
}>; }>;
export type AttrFilter<SH extends GeneralEntityShape> = { 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<{ type SorterItem = {
[K: string]: number | object | undefined; $attr: SortAttr
} & ExprOp<keyof SH>>;
export type DeduceSorterItem<SH extends GeneralEntityShape> = {
$attr: DeduceSorterAttr<SH>;
$direction?: "asc" | "desc"; $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< export type DeduceAggregation<
SH extends GeneralEntityShape, P extends Projection,
P extends DeduceProjection<SH>, F extends Filter,
F extends DeduceFilter<SH>, S extends Sorter> = Omit<Operation<'aggregate', DeduceAggregationData<P>, F, S>, 'action'>;
S extends DeduceSorter<SH>> = Omit<Operation<'aggregate', DeduceAggregationData<SH, P>, F, S>, 'action'>;
export type DeduceCreateOperationData<SH extends GeneralEntityShape> = { type CreateOperationData = {
id: string; id: string;
} & { [K: string]: any;
[k in keyof Omit<SH, InstinctiveAttributes>]?: 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> = { type UpdateOperationData = {
[k in keyof Omit<SH, InstinctiveAttributes>]?: any; id?: never;
}; [k: string]: any;
}
export type DeduceUpdateOperation<SH extends GeneralEntityShape> = Operation< export type UpdateOperation = Operation<string, UpdateOperationData, Filter, Sorter>;
'update' | string,
DeduceUpdateOperationData<SH>, DeduceFilter<SH>, DeduceSorter<SH>>;
export type DeduceRemoveOperationData<SH extends GeneralEntityShape> = { type RemoveOperationData = {
[A in keyof Omit<SH, InstinctiveAttributes>]?: any; [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> = { export type CreateOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'c'; 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> = { export type UpdateOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'u', a: 'u',
e: T; e: T;
d: DeduceUpdateOperationData<ED[T]['Schema']>; d: UpdateOperationData;
f?: DeduceFilter<ED[T]['Schema']>; f?: Filter;
}; };
export type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = { export type RemoveOpResult<ED extends EntityDict, T extends keyof ED> = {
a: 'r', a: 'r',
e: T; e: T;
f?: DeduceFilter<ED[T]['Schema']>; f?: Filter;
}; };
export type RelationHierarchy<R extends string> = { export type RelationHierarchy<R extends string> = {
[K in R]?: R[]; [K in R]?: R[];
}; };
export type CascadeRelationItem = {
cascadePath: string;
relations?: string[];
};
export type CascadeRelationAuth<R extends string> = {
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
};
// Select的级联可以去重压缩返回的数据大小 // Select的级联可以去重压缩返回的数据大小
export type SelectOpResult<ED extends EntityDict> = { export type SelectOpResult<ED extends EntityDict> = {
a: 's', a: 's',
@ -250,20 +245,3 @@ export type Configuration = {
actionType?: ActionType; actionType?: ActionType;
static?: boolean; // 标识是维表(变动较小,相对独立) 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'];
};

View File

@ -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 { export class OakOperExistedException extends OakDataException {
// 进行操作时发现同样id的Oper对象已经存在 // 进行操作时发现同样id的Oper对象已经存在
} }
@ -220,6 +232,9 @@ export function makeException(data: {
case 'OakDeadlock': { case 'OakDeadlock': {
return new OakDeadlock(data.message); return new OakDeadlock(data.message);
} }
case 'OakImportDataParseException': {
return new OakImportDataParseException(data.message!, data.line, data.header);
}
default: default:
return; return;
} }

View File

@ -16,7 +16,7 @@ export type RefOrExpression<A> = RefAttr<A> | Expression<A>;
type MathType<A> = RefOrExpression<A> | number; type MathType<A> = RefOrExpression<A> | number;
type StringType<A> = RefOrExpression<A> | string type StringType<A> = RefOrExpression<A> | string
interface Add<A> { interface Add<A> {
$add: (MathType<A> | StringType<A>)[]; $add: (MathType<A>)[];
}; };
interface Subtract<A> { interface Subtract<A> {
$subtract: [MathType<A>, MathType<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> 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>; | DateDayOfMonth<A> | DateDayOfWeek<A> | DateDiff<A> | DateCeiling<A> | DateFloor<A>;
// String
interface StringConcat<A> {
$concat: StringType<A>[];
}
type StringExpression<A> = StringConcat<A>;
//// Geo //// Geo
interface GeoContains<A> { interface GeoContains<A> {
$contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo]; $contains: [RefOrExpression<A> | Geo, RefOrExpression<A> | Geo];
@ -145,7 +154,31 @@ interface GeoDistance<A> {
type GeoExpression<A> = GeoContains<A> | 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; export type ExpressionConstant = Geo | number | Date | string | boolean;
@ -212,13 +245,35 @@ export function isMathExpression<A>(expression: any): expression is MathExpressi
return false; 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> { export function isExpression<A>(expression: any): expression is Expression<A> {
return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$'); return typeof expression === 'object' && Object.keys(expression).length === 1 && Object.keys(expression)[0].startsWith('$');
} }
export function opMultipleParams(op: string) { export function opMultipleParams(op: string) {
return !['$year', '$month', '$weekday', '$weekOfYear', '$day', '$dayOfMonth', 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 { 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': { case '$contains': {
throw new Error('$contains类型未实现'); throw new Error('$contains类型未实现');
} }
case '$concat': {
return params.join('');
}
default: { default: {
assert(false, `不能识别的expression运算符${op}`); assert(false, `不能识别的expression运算符${op}`);
} }

21
src/types/Port.ts Normal file
View File

@ -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']>;
};

View File

@ -1,5 +1,5 @@
import { ActionType } from '.'; import { ActionType } from '.';
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy } from './Entity'; import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, CascadeRelationAuth } from './Entity';
import { DataType, DataTypeParams } from './schema/DataTypes'; import { DataType, DataTypeParams } from './schema/DataTypes';
export type Ref = 'ref'; export type Ref = 'ref';
@ -47,7 +47,7 @@ export type UniqConstraint<SH extends EntityShape> = {
type?: string; type?: string;
}; };
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> { export interface StorageDesc<SH extends EntityShape> {
storageName?: string, storageName?: string,
comment?: string, comment?: string,
attributes: Attributes<SH>; attributes: Attributes<SH>;
@ -59,12 +59,14 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
static?: true; // 标识是维表(变动较小,相对独立) static?: true; // 标识是维表(变动较小,相对独立)
actions: string[]; actions: string[];
actionType: ActionType; actionType: ActionType;
relationHierarchy?: RelationHierarchy<Relation>; // relationHierarchy?: RelationHierarchy<Relation>;
// reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
relation?: string[];
// view 相关 // view 相关
view?: true; view?: true;
} }
export type StorageSchema<ED extends EntityDict> = { export type StorageSchema<ED extends EntityDict> = {
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>; [K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
} }

View File

@ -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']>; attributes?: keyof ED[T]['OpSchema'] | Array<keyof ED[T]['OpSchema']>;
check?: (operation: ED[T]['Update']) => boolean; check?: (operation: ED[T]['Update']) => boolean;
fn: (event: { operation: ED[T]['Update'] }, context: Cxt, option: OperateOption) => Promise<number> | number; 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> { 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', action: 'remove',
check?: (operation: ED[T]['Remove']) => boolean; check?: (operation: ED[T]['Remove']) => boolean;
fn: (event: { operation: ED[T]['Remove'] }, context: Cxt, option: OperateOption) => Promise<number> | number; 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> { export interface RemoveTriggerInTxn<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> extends RemoveTriggerBase<ED, T, Cxt> {

View File

@ -16,4 +16,6 @@ export * from './Exception';
export * from './Watcher'; export * from './Watcher';
export * from './AppLoader'; export * from './AppLoader';
export * from './Connector'; export * from './Connector';
export * from './Timer'; export * from './Timer';
export * from './Port';
export * from './Endpoint';

14
src/utils/date.ts Normal file
View File

@ -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);
}