Merge branch 'release'

This commit is contained in:
Xu Chang 2023-03-31 21:28:06 +08:00
commit d1829ce545
46 changed files with 1372 additions and 882 deletions

View File

@ -5,7 +5,6 @@ import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import * as ModiEntity from "../ModiEntity/Schema";
import * as OperEntity from "../OperEntity/Schema";
export declare type OpSchema = EntityShape & {
targetEntity: String<32>;
entity: String<32>;
@ -28,8 +27,6 @@ export declare type Schema = EntityShape & {
iState?: IState | null;
modiEntity$modi?: Array<ModiEntity.Schema>;
modiEntity$modi$$aggr?: AggregationResult<ModiEntity.Schema>;
operEntity$entity?: Array<OperEntity.Schema>;
operEntity$entity$$aggr?: AggregationResult<OperEntity.Schema>;
} & {
[A in ExpressionKey]?: any;
};
@ -69,12 +66,6 @@ export declare type Projection = {
modiEntity$modi$$aggr?: ModiEntity.Aggregation & {
$entity: "modiEntity";
};
operEntity$entity?: OperEntity.Selection & {
$entity: "operEntity";
};
operEntity$entity$$aggr?: OperEntity.Aggregation & {
$entity: "operEntity";
};
} & Partial<ExprOp<OpAttr | string>>;
declare type ModiIdProjection = OneOf<{
id: number;
@ -114,7 +105,6 @@ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity"
[K: string]: any;
}) & {
modiEntity$modi?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "modi" | "modiId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "modi" | "modiId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
export declare type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
@ -122,7 +112,6 @@ export declare type CreateOperation = CreateSingleOperation | CreateMultipleOper
export declare type UpdateOperationData = FormUpdateData<OpSchema> & {
[k: string]: any;
modiEntity$modi?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "modi" | "modiId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "modi" | "modiId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type UpdateOperation = OakOperation<"update" | ParticularAction | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {};

View File

@ -12,6 +12,7 @@ export declare type OpSchema = EntityShape & {
filter?: Object | null;
extra?: Object | null;
operatorId?: ForeignKey<"user"> | null;
targetEntity: String<32>;
};
export declare type OpAttr = keyof OpSchema;
export declare type Schema = EntityShape & {
@ -20,6 +21,7 @@ export declare type Schema = EntityShape & {
filter?: Object | null;
extra?: Object | null;
operatorId?: ForeignKey<"user"> | null;
targetEntity: String<32>;
operator?: User.Schema | null;
operEntity$oper?: Array<OperEntity.Schema>;
operEntity$oper$$aggr?: AggregationResult<OperEntity.Schema>;
@ -37,6 +39,7 @@ declare type AttrFilter = {
extra: Object;
operatorId: Q_StringValue | SubQuery.UserIdSubQuery;
operator: User.Filter;
targetEntity: Q_StringValue;
};
export declare type Filter = MakeFilter<AttrFilter & ExprOp<OpAttr | string>>;
export declare type Projection = {
@ -52,6 +55,7 @@ export declare type Projection = {
extra?: number;
operatorId?: number;
operator?: User.Projection;
targetEntity?: number;
operEntity$oper?: OperEntity.Selection & {
$entity: "operEntity";
};
@ -79,6 +83,8 @@ export declare type SortAttr = {
operatorId: number;
} | {
operator: User.SortAttr;
} | {
targetEntity: number;
} | {
[k: string]: any;
} | OneOf<ExprOp<OpAttr | string>>;

View File

@ -22,6 +22,12 @@ exports.desc = {
operatorId: {
type: "ref",
ref: "user"
},
targetEntity: {
type: "varchar",
params: {
length: 32
}
}
},
actionType: "appendOnly",

View File

@ -5,21 +5,19 @@ import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape } from "../../types/Entity";
import { AppendOnlyAction } from "../../actions/action";
import * as Oper from "../Oper/Schema";
import * as Modi from "../Modi/Schema";
import * as User from "../User/Schema";
import * as UserEntityGrant from "../UserEntityGrant/Schema";
export declare type OpSchema = EntityShape & {
operId: ForeignKey<"oper">;
entity: "modi" | "user" | "userEntityGrant" | string;
entity: "user" | "userEntityGrant" | string;
entityId: String<64>;
};
export declare type OpAttr = keyof OpSchema;
export declare type Schema = EntityShape & {
operId: ForeignKey<"oper">;
entity: "modi" | "user" | "userEntityGrant" | string;
entity: "user" | "userEntityGrant" | string;
entityId: String<64>;
oper: Oper.Schema;
modi?: Modi.Schema;
user?: User.Schema;
userEntityGrant?: UserEntityGrant.Schema;
} & {
@ -34,11 +32,10 @@ declare type AttrFilter<E> = {
oper: Oper.Filter;
entity: E;
entityId: Q_StringValue;
modi: Modi.Filter;
user: User.Filter;
userEntityGrant: UserEntityGrant.Filter;
};
export declare type Filter<E = Q_EnumValue<"modi" | "user" | "userEntityGrant" | string>> = MakeFilter<AttrFilter<E> & ExprOp<OpAttr | string>>;
export declare type Filter<E = Q_EnumValue<"user" | "userEntityGrant" | string>> = MakeFilter<AttrFilter<E> & ExprOp<OpAttr | string>>;
export declare type Projection = {
"#id"?: NodeId;
[k: string]: any;
@ -50,7 +47,6 @@ export declare type Projection = {
oper?: Oper.Projection;
entity?: number;
entityId?: number;
modi?: Modi.Projection;
user?: User.Projection;
userEntityGrant?: UserEntityGrant.Projection;
} & Partial<ExprOp<OpAttr | string>>;
@ -60,9 +56,6 @@ declare type OperEntityIdProjection = OneOf<{
declare type OperIdProjection = OneOf<{
operId: number;
}>;
declare type ModiIdProjection = OneOf<{
entityId: number;
}>;
declare type UserIdProjection = OneOf<{
entityId: number;
}>;
@ -85,8 +78,6 @@ export declare type SortAttr = {
entity: number;
} | {
entityId: number;
} | {
modi: Modi.SortAttr;
} | {
user: User.SortAttr;
} | {
@ -108,17 +99,6 @@ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity"
} | {
operId: String<64>;
})) & ({
entity?: never;
entityId?: never;
modi: Modi.CreateSingleOperation;
} | {
entity: "modi";
entityId: String<64>;
modi: Modi.UpdateOperation;
} | {
entity: "modi";
entityId: String<64>;
} | {
entity?: never;
entityId?: never;
user: User.CreateSingleOperation;
@ -155,10 +135,6 @@ export declare type UpdateOperationData = FormUpdateData<Omit<OpSchema, "entity"
oper?: never;
operId?: String<64> | null;
})) & ({
modi?: Modi.CreateSingleOperation | Modi.UpdateOperation | Modi.RemoveOperation;
entityId?: never;
entity?: never;
} | {
user?: User.CreateSingleOperation | User.UpdateOperation | User.RemoveOperation;
entityId?: never;
entity?: never;
@ -167,15 +143,13 @@ export declare type UpdateOperationData = FormUpdateData<Omit<OpSchema, "entity"
entityId?: never;
entity?: never;
} | {
entity?: ("modi" | "user" | "userEntityGrant" | string) | null;
entity?: ("user" | "userEntityGrant" | string) | null;
entityId?: String<64> | null;
}) & {
[k: string]: any;
};
export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {} & ({
modi?: Modi.UpdateOperation | Modi.RemoveOperation;
} | {
user?: User.UpdateOperation | User.RemoveOperation;
} | {
userEntityGrant?: UserEntityGrant.UpdateOperation | UserEntityGrant.RemoveOperation;
@ -185,7 +159,6 @@ export declare type RemoveOperationData = {} & ({
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation;
export declare type OperIdSubQuery = Selection<OperIdProjection>;
export declare type ModiIdSubQuery = Selection<ModiIdProjection>;
export declare type UserIdSubQuery = Selection<UserIdProjection>;
export declare type UserEntityGrantIdSubQuery = Selection<UserEntityGrantIdProjection>;
export declare type OperEntityIdSubQuery = Selection<OperEntityIdProjection>;

View File

@ -13,7 +13,7 @@ exports.desc = {
params: {
length: 32
},
ref: ["modi", "user", "userEntityGrant"]
ref: ["user", "userEntityGrant"]
},
entityId: {
type: "varchar",

View File

@ -6,8 +6,8 @@ import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOper
import { Action, ParticularAction, UserState } from "./Action";
import { RelationAction } from "../../actions/action";
import * as Oper from "../Oper/Schema";
import * as OperEntity from "../OperEntity/Schema";
import * as ModiEntity from "../ModiEntity/Schema";
import * as OperEntity from "../OperEntity/Schema";
export declare type OpSchema = EntityShape & {
name?: String<16> | null;
nickname?: String<64> | null;
@ -27,10 +27,10 @@ export declare type Schema = EntityShape & {
oper$operator$$aggr?: AggregationResult<Oper.Schema>;
user$ref?: Array<Schema>;
user$ref$$aggr?: AggregationResult<Schema>;
operEntity$entity?: Array<OperEntity.Schema>;
operEntity$entity$$aggr?: AggregationResult<OperEntity.Schema>;
modiEntity$entity?: Array<ModiEntity.Schema>;
modiEntity$entity$$aggr?: AggregationResult<ModiEntity.Schema>;
operEntity$entity?: Array<OperEntity.Schema>;
operEntity$entity$$aggr?: AggregationResult<OperEntity.Schema>;
} & {
[A in ExpressionKey]?: any;
};
@ -72,18 +72,18 @@ export declare type Projection = {
user$ref$$aggr?: Aggregation & {
$entity: "user";
};
operEntity$entity?: OperEntity.Selection & {
$entity: "operEntity";
};
operEntity$entity$$aggr?: OperEntity.Aggregation & {
$entity: "operEntity";
};
modiEntity$entity?: ModiEntity.Selection & {
$entity: "modiEntity";
};
modiEntity$entity$$aggr?: ModiEntity.Aggregation & {
$entity: "modiEntity";
};
operEntity$entity?: OperEntity.Selection & {
$entity: "operEntity";
};
operEntity$entity$$aggr?: OperEntity.Aggregation & {
$entity: "operEntity";
};
} & Partial<ExprOp<OpAttr | string>>;
declare type UserIdProjection = OneOf<{
id: number;
@ -131,8 +131,8 @@ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "refId">
})) & {
oper$operator?: OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">[]> | Array<OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">>>;
user$ref?: OakOperation<UpdateOperation["action"], Omit<UpdateOperationData, "ref" | "refId">, Filter> | OakOperation<"create", Omit<CreateOperationData, "ref" | "refId">[]> | Array<OakOperation<"create", Omit<CreateOperationData, "ref" | "refId">> | OakOperation<UpdateOperation["action"], Omit<UpdateOperationData, "ref" | "refId">, Filter>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
modiEntity$entity?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
export declare type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
@ -153,8 +153,8 @@ export declare type UpdateOperationData = FormUpdateData<Omit<OpSchema, "refId">
[k: string]: any;
oper$operator?: OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">[]> | Array<OakOperation<"create", Omit<Oper.CreateOperationData, "operator" | "operatorId">>>;
user$ref?: UpdateOperation | RemoveOperation | OakOperation<"create", Omit<CreateOperationData, "ref" | "refId">[]> | Array<OakOperation<"create", Omit<CreateOperationData, "ref" | "refId">> | UpdateOperation | RemoveOperation>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
modiEntity$entity?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type UpdateOperation = OakOperation<"update" | ParticularAction | RelationAction | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {} & (({

View File

@ -4,8 +4,8 @@ import { OneOf } from "../../types/Polyfill";
import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity";
import { GenericAction } from "../../actions/action";
import * as OperEntity from "../OperEntity/Schema";
import * as ModiEntity from "../ModiEntity/Schema";
import * as OperEntity from "../OperEntity/Schema";
export declare type OpSchema = EntityShape & {
entity: String<32>;
entityId: String<64>;
@ -16,10 +16,10 @@ export declare type Schema = EntityShape & {
entity: String<32>;
entityId: String<64>;
relation: String<32>;
operEntity$entity?: Array<OperEntity.Schema>;
operEntity$entity$$aggr?: AggregationResult<OperEntity.Schema>;
modiEntity$entity?: Array<ModiEntity.Schema>;
modiEntity$entity$$aggr?: AggregationResult<ModiEntity.Schema>;
operEntity$entity?: Array<OperEntity.Schema>;
operEntity$entity$$aggr?: AggregationResult<OperEntity.Schema>;
} & {
[A in ExpressionKey]?: any;
};
@ -43,18 +43,18 @@ export declare type Projection = {
entity?: number;
entityId?: number;
relation?: number;
operEntity$entity?: OperEntity.Selection & {
$entity: "operEntity";
};
operEntity$entity$$aggr?: OperEntity.Aggregation & {
$entity: "operEntity";
};
modiEntity$entity?: ModiEntity.Selection & {
$entity: "modiEntity";
};
modiEntity$entity$$aggr?: ModiEntity.Aggregation & {
$entity: "modiEntity";
};
operEntity$entity?: OperEntity.Selection & {
$entity: "operEntity";
};
operEntity$entity$$aggr?: OperEntity.Aggregation & {
$entity: "operEntity";
};
} & Partial<ExprOp<OpAttr | string>>;
declare type UserEntityGrantIdProjection = OneOf<{
id: number;
@ -89,16 +89,16 @@ export declare type CreateOperationData = FormCreateData<Omit<OpSchema, "entity"
entityId?: string;
[K: string]: any;
}) & {
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
modiEntity$entity?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
export declare type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
export declare type UpdateOperationData = FormUpdateData<OpSchema> & {
[k: string]: any;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
modiEntity$entity?: OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<ModiEntity.CreateOperationData, "entity" | "entityId">>>;
operEntity$entity?: OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">[]> | Array<OakOperation<"create", Omit<OperEntity.CreateOperationData, "entity" | "entityId">>>;
};
export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {};

View File

@ -5,6 +5,7 @@ export declare const ENTITY_PATH_IN_OAK_DOMAIN: () => string;
export declare const TYPE_PATH_IN_OAK_DOMAIN: (level?: number) => string;
export declare const ACTION_CONSTANT_IN_OAK_DOMAIN: (level?: number) => string;
export declare const RESERVED_ENTITIES: string[];
export declare const ENTITY_NAME_MAX_LENGTH = 32;
export declare const STRING_LITERAL_MAX_LENGTH = 24;
export declare const NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
export declare const NUMERICAL_LITERL_DEFAULT_SCALE = 2;

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.RESERVED_ENTITIES = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0;
exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.ENTITY_NAME_MAX_LENGTH = exports.RESERVED_ENTITIES = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0;
exports.LIB_OAK_DOMAIN = 'oak-domain';
var LIB_OAK_GENERAL_BUSINESS = 'oak-general-business';
var LIB_PATH = function () { return 'lib'; };
@ -33,6 +33,7 @@ exports.ACTION_CONSTANT_IN_OAK_DOMAIN = ACTION_CONSTANT_IN_OAK_DOMAIN;
// export const OUTPUT_PATH = 'app-domain/entities';
exports.RESERVED_ENTITIES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
'Locale', 'Projection', 'Data'];
exports.ENTITY_NAME_MAX_LENGTH = 32;
exports.STRING_LITERAL_MAX_LENGTH = 24;
exports.NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
exports.NUMERICAL_LITERL_DEFAULT_SCALE = 2;

View File

@ -344,6 +344,18 @@ function getStringEnumValues(filename, program, obj, node) {
}
}
}
function checkNameLegal(filename, attrName, upperCase) {
(0, assert_1.default)(attrName.length <= env_1.ENTITY_NAME_MAX_LENGTH, "\u6587\u4EF6\u300C".concat(filename, "\u300D\uFF1A\u300C").concat(attrName, "\u300D\u7684\u540D\u79F0\u5B9A\u4E49\u8FC7\u957F\uFF0C\u4E0D\u80FD\u8D85\u8FC7\u300C").concat(env_1.ENTITY_NAME_MAX_LENGTH, "\u300D\u957F\u5EA6"));
if (upperCase) {
(0, assert_1.default)(/[A-Z][a-z|A-Z|0-9]+/i.test(attrName), "\u6587\u4EF6\u300C".concat(filename, "\u300D\uFF1A\u300C").concat(attrName, "\u300D\u7684\u540D\u79F0\u5FC5\u987B\u4EE5\u5927\u5199\u5B57\u6BCD\u5F00\u59CB\uFF0C\u4E14\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57"));
}
else if (upperCase === false) {
(0, assert_1.default)(/[a-z][a-z|A-Z|0-9]+/i.test(attrName), "\u6587\u4EF6\u300C".concat(filename, "\u300D\uFF1A\u300C").concat(attrName, "\u300D\u7684\u540D\u79F0\u5FC5\u987B\u4EE5\u5C0F\u5199\u5B57\u6BCD\u5F00\u59CB\uFF0C\u4E14\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57"));
}
else {
(0, assert_1.default)(/[a-z|A-Z][a-z|A-Z|0-9]+/i.test(attrName), "\u6587\u4EF6\u300C".concat(filename, "\u300D\uFF1A\u300C").concat(attrName, "\u300D\u7684\u540D\u79F0\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u59CB\uFF0C\u4E14\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57"));
}
}
function analyzeEntity(filename, path, program, relativePath) {
var _a;
var fullPath = "".concat(path, "/").concat(filename);
@ -355,6 +367,7 @@ function analyzeEntity(filename, path, program, relativePath) {
// removeFromRelationShip(moduleName);
console.warn("\u51FA\u73B0\u4E86\u540C\u540D\u7684Entity\u5B9A\u4E49\u300C".concat(moduleName, "\u300D\uFF0C\u5C06\u4F7F\u7528").concat(fullPath, "\u53D6\u4EE3\u6389\u9ED8\u8BA4\u5BF9\u8C61\uFF0C\u8BF7\u68C0\u67E5\u65B0\u7684\u5BF9\u8C61\u7ED3\u6784\u53CA\u76F8\u5173\u5E38\u91CF\u5B9A\u4E49\u4E0E\u539F\u6709\u7684\u517C\u5BB9\uFF0C\u5426\u5219\u539F\u6709\u5BF9\u8C61\u7684\u76F8\u5173\u903B\u8F91\u4F1A\u51FA\u73B0\u4E0D\u53EF\u77E5\u5F02\u5E38"));
}
checkNameLegal(filename, moduleName, true);
var referencedSchemas = [];
var schemaAttrs = [];
var hasFulltextIndex = false;
@ -406,6 +419,7 @@ function analyzeEntity(filename, path, program, relativePath) {
var _a, _b;
var _c = attrNode, type = _c.type, name = _c.name, questionToken = _c.questionToken;
var attrName = name.text;
checkNameLegal(filename, attrName, false);
if (ts.isTypeReferenceNode(type)
&& ts.isIdentifier(type.typeName)) {
if ((referencedSchemas.includes(type.typeName.text) || type.typeName.text === 'Schema')) {
@ -516,27 +530,27 @@ function analyzeEntity(filename, path, program, relativePath) {
_a[moduleName] = 1,
_a));
}
else if (hasEntityAttr_1 || hasEntityIdAttr_1) {
throw new Error("\u6587\u4EF6\u300C".concat(filename, "\u300D\uFF1A\u5C5E\u6027 \u5B9A\u4E49\u4E2D\u53EA\u5305\u542B").concat(hasEntityAttr_1 ? 'entity' : 'entityId', "\uFF0C\u4E0D\u7B26\u5408\u5B9A\u4E49\u89C4\u8303\u3002entity/entityId\u5FC5\u987B\u8054\u5408\u51FA\u73B0\uFF0C\u4EE3\u8868\u4E0D\u5B9A\u5BF9\u8C61\u7684\u53CD\u5411\u6307\u9488"));
}
beforeSchema = false;
// 对于不是Oper的对象全部建立和OperEntity的反指关系
if (!['Oper', 'OperEntity', 'ModiEntity'].includes(moduleName)) {
if (ReversePointerRelations['OperEntity'] && !ReversePointerRelations['OperEntity'].includes(moduleName)) {
ReversePointerRelations['OperEntity'].push(moduleName);
}
else {
(0, lodash_1.assign)(ReversePointerRelations, (_b = {},
_b['OperEntity'] = [moduleName],
_b));
}
// 对于不是Modi的对象全部建立和ModiEntity的反指关系
if (!['Modi'].includes(moduleName) && !toModi) {
// 对于不是Modi和Oper的对象全部建立和ModiEntity的反指关系
if (!['Modi', 'Oper', 'OperEntity', 'ModiEntity'].includes(moduleName) && !toModi) {
if (ReversePointerRelations['ModiEntity'] && !ReversePointerRelations['ModiEntity'].includes(moduleName)) {
ReversePointerRelations['ModiEntity'].push(moduleName);
}
else {
(0, lodash_1.assign)(ReversePointerRelations, (_c = {},
_c['ModiEntity'] = [moduleName],
_c));
(0, lodash_1.assign)(ReversePointerRelations, (_b = {},
_b['ModiEntity'] = [moduleName],
_b));
}
if (ReversePointerRelations['OperEntity'] && !ReversePointerRelations['OperEntity'].includes(moduleName)) {
ReversePointerRelations['OperEntity'].push(moduleName);
}
else {
(0, lodash_1.assign)(ReversePointerRelations, (_c = {},
_c['OperEntity'] = [moduleName],
_c));
}
}
}

View File

@ -7,4 +7,5 @@ export interface Schema extends EntityShape {
filter?: Object;
extra?: Object;
operator?: User;
targetEntity: String<32>;
}

View File

@ -13,6 +13,7 @@ var locale = {
filter: '选择条件',
extra: '其它',
operator: '操作者',
targetEntity: '关联对象',
},
},
};

View File

@ -2,7 +2,7 @@
import { EntityDict, RowStore, OperateOption, OperationResult, SelectOption, Context, TxnOption, OpRecord, AggregationResult } from "../types";
import { IncomingHttpHeaders } from "http";
export declare abstract class AsyncContext<ED extends EntityDict> implements Context {
private rowStore;
rowStore: AsyncRowStore<ED, this>;
private uuid?;
opRecords: OpRecord<ED>[];
private scene?;
@ -12,6 +12,10 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
commit: Array<() => Promise<void>>;
rollback: Array<() => Promise<void>>;
};
/**
*
*/
abstract refineOpRecords(): Promise<void>;
constructor(store: AsyncRowStore<ED, AsyncContext<ED>>, headers?: IncomingHttpHeaders);
setHeaders(headers: IncomingHttpHeaders): void;
getHeader(key: string): string | string[] | undefined;

View File

@ -1,6 +1,6 @@
import { EntityDict, OperateOption, SelectOption, OperationResult, AggregationResult } from "../types/Entity";
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { RowStore } from '../types/RowStore';
import { OperationRewriter, RowStore, SelectionRewriter } from '../types/RowStore';
import { StorageSchema } from '../types/Storage';
import { SyncContext } from "./SyncRowStore";
import { AsyncContext } from "./AsyncRowStore";
@ -9,6 +9,12 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
constructor(storageSchema: StorageSchema<ED>);
protected abstract supportManyToOneJoin(): boolean;
protected abstract supportMultipleCreate(): boolean;
private selectionRewriters;
private operationRewriters;
private reinforceSelection;
private reinforceOperation;
registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
protected abstract selectAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
protected abstract updateAbjointRow<T extends keyof ED, OP extends OperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): number;
protected abstract selectAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]['Schema']>[]>;

View File

@ -11,13 +11,251 @@ var types_1 = require("../types");
var lodash_1 = require("../utils/lodash");
var filter_2 = require("./filter");
var uuid_1 = require("../utils/uuid");
var selection_1 = require("./selection");
/**这个用来处理级联的select和update对不同能力的 */
var CascadeStore = /** @class */ (function (_super) {
tslib_1.__extends(CascadeStore, _super);
function CascadeStore(storageSchema) {
return _super.call(this, storageSchema) || this;
var _this = _super.call(this, storageSchema) || this;
_this.selectionRewriters = [];
_this.operationRewriters = [];
return _this;
}
CascadeStore.prototype.reinforceSelection = function (entity, selection) {
var _this = this;
var filter = selection.filter, data = selection.data, sorter = selection.sorter;
var checkNode = function (projectionNode, attrs) {
attrs.forEach(function (attr) {
var _a;
if (!projectionNode.hasOwnProperty(attr)) {
Object.assign(projectionNode, (_a = {},
_a[attr] = 1,
_a));
}
});
};
var relevantIds = [];
if (filter) {
var toBeAssignNode_1 = {}; // 用来记录在表达式中涉及到的结点
// filter当中所关联到的属性必须在projection中
var filterNodeDict_1 = {};
var checkFilterNode_1 = function (entity2, filterNode, projectionNode) {
var _a, e_1, _b, _c, _d, _e, _f;
var necessaryAttrs = ['id'];
for (var attr in filterNode) {
if (attr === '#id') {
(0, assert_1.default)(!filterNodeDict_1[filterNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(filterNode[attr]));
Object.assign(filterNodeDict_1, (_a = {},
_a[filterNode[attr]] = projectionNode,
_a));
if (toBeAssignNode_1[filterNode[attr]]) {
checkNode(projectionNode, toBeAssignNode_1[filterNode[attr]]);
}
}
else if (['$and', '$or'].includes(attr)) {
try {
for (var _g = (e_1 = void 0, tslib_1.__values(filterNode[attr])), _h = _g.next(); !_h.done; _h = _g.next()) {
var node = _h.value;
checkFilterNode_1(entity2, node, projectionNode);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
}
finally { if (e_1) throw e_1.error; }
}
}
else if (attr === '$not') {
checkFilterNode_1(entity2, filterNode[attr], projectionNode);
}
else if (attr === '$text') {
// 全文检索首先要有fulltext索引其次要把fulltext的相关属性加到projection里
var indexes = _this.getSchema()[entity2].indexes;
var fulltextIndex = indexes.find(function (ele) { return ele.config && ele.config.type === 'fulltext'; });
var attributes = fulltextIndex.attributes;
necessaryAttrs.push.apply(necessaryAttrs, tslib_1.__spreadArray([], tslib_1.__read((attributes.map(function (ele) { return ele.name; }))), false));
}
else {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
var exprResult = (0, types_1.getAttrRefInExpression)(filterNode[attr]);
for (var nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (filterNodeDict_1[nodeName]) {
checkNode(filterNodeDict_1[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode_1[nodeName]) {
(_c = toBeAssignNode_1[nodeName]).push.apply(_c, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
}
else {
Object.assign(toBeAssignNode_1, (_d = {},
_d[nodeName] = exprResult[nodeName],
_d));
}
}
}
}
else {
var rel = _this.judgeRelation(entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
if (!projectionNode[attr]) {
Object.assign(projectionNode, (_e = {},
_e[attr] = {
id: 1,
},
_e));
}
checkFilterNode_1(attr, filterNode[attr], projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push("".concat(attr, "Id"));
if (!projectionNode[attr]) {
Object.assign(projectionNode, (_f = {},
_f[attr] = {
id: 1,
},
_f));
}
checkFilterNode_1(rel, filterNode[attr], projectionNode[attr]);
}
else if (rel instanceof Array) {
// 现在filter中还不支持一对多的语义先放着吧
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
};
checkFilterNode_1(entity, filter, data);
relevantIds = (0, filter_2.getRelevantIds)(filter);
}
// sorter感觉现在取不取影响不大前端的list直接获取返回的ids了先不管之
if (sorter) {
}
var toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
var projectionNodeDict = {};
var checkProjectionNode = function (entity2, projectionNode) {
var _a, _b, _c;
var necessaryAttrs = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据有时两个页面的filter的差异会导致有一个加createAt有一个不加此时可能产生前台取数据不完整的异常。先统一加上
for (var attr in projectionNode) {
if (attr === '#id') {
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
Object.assign(projectionNodeDict, (_a = {},
_a[projectionNode[attr]] = projectionNode,
_a));
if (toBeAssignNode2[projectionNode[attr]]) {
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]]);
}
}
else {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
var exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
for (var nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (projectionNodeDict[nodeName]) {
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode2[nodeName]) {
(_b = toBeAssignNode2[nodeName]).push.apply(_b, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
}
else {
Object.assign(toBeAssignNode2, (_c = {},
_c[nodeName] = exprResult[nodeName],
_c));
}
}
}
}
else {
var rel = (0, relation_1.judgeRelation)(_this.getSchema(), entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
checkProjectionNode(attr, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push("".concat(attr, "Id"));
checkProjectionNode(rel, projectionNode[attr]);
}
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
var data_1 = projectionNode[attr].data;
if (rel[1]) {
checkNode(data_1, [rel[1]]);
}
else {
checkNode(data_1, ['entity', 'entityId']);
}
_this.reinforceSelection(rel[0], projectionNode[attr]);
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
// 如果对象中指向一对多的Modi此时加上指向Modi的projection
if (_this.getSchema()[entity2].toModi) {
Object.assign(projectionNode, {
modi$entity: {
$entity: 'modi',
data: {
id: 1,
targetEntity: 1,
entity: 1,
entityId: 1,
action: 1,
iState: 1,
data: 1,
filter: 1,
},
filter: {
iState: 'active',
},
}
});
}
};
checkProjectionNode(entity, data);
if (!sorter && relevantIds.length === 0) {
// 如果没有sorter就给予一个按createAt逆序的sorter
Object.assign(selection, {
sorter: [
{
$attr: {
$$createAt$$: 1,
},
$direction: 'desc',
}
]
});
Object.assign(data, {
$$createAt$$: 1,
});
}
this.selectionRewriters.forEach(function (ele) { return ele(_this.getSchema(), entity, selection); });
};
CascadeStore.prototype.reinforceOperation = function (entity, operation) {
var _this = this;
this.operationRewriters.forEach(function (ele) { return ele(_this.getSchema(), entity, operation); });
};
CascadeStore.prototype.registerOperationRewriter = function (rewriter) {
this.operationRewriters.push(rewriter);
};
CascadeStore.prototype.registerSelectionRewriter = function (rewriter) {
this.selectionRewriters.push(rewriter);
};
CascadeStore.prototype.destructCascadeSelect = function (entity, projection2, context, cascadeSelectFn, aggregateFn, option) {
var _this = this;
var projection = {};
@ -69,6 +307,10 @@ var CascadeStore = /** @class */ (function (_super) {
}
else {
cascadeSelectionFns.push(function (result) {
var entityIds = (0, lodash_1.uniq)(result.filter(function (ele) { return ele.entity === attr; }).map(function (ele) {
(0, assert_1.default)(ele.entityId !== null);
return ele.entityId;
}));
var dealWithSubRows = function (subRows) {
(0, assert_1.default)(subRows.length <= entityIds.length);
if (subRows.length < entityIds.length && !toModi) {
@ -101,10 +343,6 @@ var CascadeStore = /** @class */ (function (_super) {
}
});
};
var entityIds = (0, lodash_1.uniq)(result.filter(function (ele) { return ele.entity === attr; }).map(function (ele) {
(0, assert_1.default)(ele.entityId !== null);
return ele.entityId;
}));
if (entityIds.length > 0) {
var subRows = cascadeSelectFn.call(_this, attr, {
data: projection2[attr],
@ -121,8 +359,6 @@ var CascadeStore = /** @class */ (function (_super) {
dealWithSubRows(subRows);
}
}
else {
}
});
}
}
@ -161,6 +397,7 @@ var CascadeStore = /** @class */ (function (_super) {
}
else {
cascadeSelectionFns.push(function (result) {
var ids = (0, lodash_1.uniq)(result.filter(function (ele) { return !!(ele["".concat(attr, "Id")]); }).map(function (ele) { return ele["".concat(attr, "Id")]; }));
var dealWithSubRows = function (subRows) {
(0, assert_1.default)(subRows.length <= ids.length);
if (subRows.length < ids.length && !toModi) {
@ -198,7 +435,6 @@ var CascadeStore = /** @class */ (function (_super) {
}
});
};
var ids = (0, lodash_1.uniq)(result.filter(function (ele) { return !!(ele["".concat(attr, "Id")]); }).map(function (ele) { return ele["".concat(attr, "Id")]; }));
if (ids.length > 0) {
var subRows = cascadeSelectFn.call(_this, relation, {
data: projection2[attr],
@ -262,6 +498,15 @@ var CascadeStore = /** @class */ (function (_super) {
var _a;
var ids = result.map(function (ele) { return ele.id; });
var dealWithSubRows = function (subRows) {
var _a;
// 这里如果result只有一行则把返回结果直接置上不对比外键值
// 这样做的原因是有的对象的filter会被改写掉userId),只能临时这样处理
if (result.length == 1) {
Object.assign(result[0], (_a = {},
_a[attr] = subRows,
_a));
}
else {
result.forEach(function (ele) {
var _a;
var subRowss = subRows.filter(function (ele2) { return ele2[foreignKey_1] === ele.id; });
@ -270,6 +515,7 @@ var CascadeStore = /** @class */ (function (_super) {
_a[attr] = subRowss,
_a));
});
}
};
if (ids.length > 0) {
var subRows = cascadeSelectFn.call(_this, entity2_1, {
@ -332,6 +578,15 @@ var CascadeStore = /** @class */ (function (_super) {
cascadeSelectionFns.push(function (result) {
var ids = result.map(function (ele) { return ele.id; });
var dealWithSubRows = function (subRows) {
var _a;
// 这里如果result只有一行则把返回结果直接置上不对比外键值
// 这样做的原因是有的对象的filter会被改写掉userId),只能临时这样处理
if (result.length === 1) {
Object.assign(result[0], (_a = {},
_a[attr] = subRows,
_a));
}
else {
result.forEach(function (ele) {
var _a;
var subRowss = subRows.filter(function (ele2) { return ele2.entityId === ele.id; });
@ -340,6 +595,7 @@ var CascadeStore = /** @class */ (function (_super) {
_a[attr] = subRowss,
_a));
});
}
};
if (ids.length > 0) {
var subRows = cascadeSelectFn.call(_this, entity2_1, {
@ -426,7 +682,7 @@ var CascadeStore = /** @class */ (function (_super) {
option2.modiParentEntity = entity;
}
var _loop_2 = function (attr) {
var _a, _b, _c, e_1, _d;
var _a, _b, _c, e_2, _d;
var relation = (0, relation_1.judgeRelation)(this_2.storageSchema, entity, attr);
if (relation === 1) {
Object.assign(opData, (_a = {},
@ -683,17 +939,17 @@ var CascadeStore = /** @class */ (function (_super) {
};
if (otmOperations instanceof Array) {
try {
for (var otmOperations_1 = (e_1 = void 0, tslib_1.__values(otmOperations)), otmOperations_1_1 = otmOperations_1.next(); !otmOperations_1_1.done; otmOperations_1_1 = otmOperations_1.next()) {
for (var otmOperations_1 = (e_2 = void 0, tslib_1.__values(otmOperations)), otmOperations_1_1 = otmOperations_1.next(); !otmOperations_1_1.done; otmOperations_1_1 = otmOperations_1.next()) {
var oper = otmOperations_1_1.value;
dealWithOneToMany(oper);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (otmOperations_1_1 && !otmOperations_1_1.done && (_d = otmOperations_1.return)) _d.call(otmOperations_1);
}
finally { if (e_1) throw e_1.error; }
finally { if (e_2) throw e_2.error; }
}
}
else {
@ -754,8 +1010,8 @@ var CascadeStore = /** @class */ (function (_super) {
*/
CascadeStore.prototype.doUpdateSingleRowAsync = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var data, action, operId, filter, now, _a, modiCreate, result_1, createInner, multipleCreate, data_1, data_1_1, d, createSingleOper, e_2_1, operatorId, createOper, _b, ids_1, selection, rows, modiUpsert, upsertModis, _c, originData, originId, _d, createOper, updateAttrCount, result;
var e_2, _e, _f, _g, _h, _j, _k, _l;
var data, action, operId, filter, now, _a, modiCreate, result_1, createInner, multipleCreate, data_2, data_2_1, d, createSingleOper, e_3_1, operatorId, createOper, _b, ids_1, selection, rows, modiUpsert, upsertModis, _c, originData, originId, _d, createOper, updateAttrCount, result;
var e_3, _e, _f, _g, _h, _j, _k, _l;
var _this = this;
return tslib_1.__generator(this, function (_m) {
switch (_m.label) {
@ -766,7 +1022,7 @@ var CascadeStore = /** @class */ (function (_super) {
switch (_a) {
case 'create': return [3 /*break*/, 1];
}
return [3 /*break*/, 23];
return [3 /*break*/, 22];
case 1:
this.preProcessDataCreated(entity, data);
if (!(option.modiParentEntity && !['modi', 'modiEntity', 'oper', 'operEntity'].includes(entity))) return [3 /*break*/, 3];
@ -797,7 +1053,7 @@ var CascadeStore = /** @class */ (function (_super) {
case 3:
result_1 = 0;
createInner = function (operation2) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, e_3;
var _a, e_4;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
@ -808,7 +1064,7 @@ var CascadeStore = /** @class */ (function (_super) {
result_1 = _a + _b.sent();
return [3 /*break*/, 3];
case 2:
e_3 = _b.sent();
e_4 = _b.sent();
/*
if (e instanceof OakCongruentRowExists) {
if (option.allowExists) {
@ -872,7 +1128,7 @@ var CascadeStore = /** @class */ (function (_super) {
}
}
} */
throw e_3;
throw e_4;
case 3: return [2 /*return*/];
}
});
@ -886,11 +1142,11 @@ var CascadeStore = /** @class */ (function (_super) {
return [3 /*break*/, 12];
case 5:
_m.trys.push([5, 10, 11, 12]);
data_1 = tslib_1.__values(data), data_1_1 = data_1.next();
data_2 = tslib_1.__values(data), data_2_1 = data_2.next();
_m.label = 6;
case 6:
if (!!data_1_1.done) return [3 /*break*/, 9];
d = data_1_1.value;
if (!!data_2_1.done) return [3 /*break*/, 9];
d = data_2_1.value;
createSingleOper = {
id: 'any',
action: 'create',
@ -901,18 +1157,18 @@ var CascadeStore = /** @class */ (function (_super) {
_m.sent();
_m.label = 8;
case 8:
data_1_1 = data_1.next();
data_2_1 = data_2.next();
return [3 /*break*/, 6];
case 9: return [3 /*break*/, 12];
case 10:
e_2_1 = _m.sent();
e_2 = { error: e_2_1 };
e_3_1 = _m.sent();
e_3 = { error: e_3_1 };
return [3 /*break*/, 12];
case 11:
try {
if (data_1_1 && !data_1_1.done && (_e = data_1.return)) _e.call(data_1);
if (data_2_1 && !data_2_1.done && (_e = data_2.return)) _e.call(data_2);
}
finally { if (e_2) throw e_2.error; }
finally { if (e_3) throw e_3.error; }
return [7 /*endfinally*/];
case 12: return [3 /*break*/, 15];
case 13: return [4 /*yield*/, createInner(operation)];
@ -927,13 +1183,11 @@ var CascadeStore = /** @class */ (function (_super) {
d: data,
});
}
if (!(!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity))) return [3 /*break*/, 22];
if (!(!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity))) return [3 /*break*/, 21];
// 按照框架要求生成Oper和OperEntity这两个内置的对象
(0, assert_1.default)(operId);
return [4 /*yield*/, context.getCurrentUserId(true)];
case 16:
operatorId = _m.sent();
if (!operatorId) return [3 /*break*/, 22];
operatorId = context.getCurrentUserId(true);
if (!operatorId) return [3 /*break*/, 21];
_f = {
id: 'dummy',
action: 'create'
@ -942,9 +1196,10 @@ var CascadeStore = /** @class */ (function (_super) {
id: operId,
action: action,
data: data,
operatorId: operatorId
operatorId: operatorId,
targetEntity: entity
};
if (!(data instanceof Array)) return [3 /*break*/, 18];
if (!(data instanceof Array)) return [3 /*break*/, 17];
_h = {
id: 'dummy',
action: 'create'
@ -957,31 +1212,31 @@ var CascadeStore = /** @class */ (function (_super) {
_a = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 1: return [2 /*return*/, (_a.id = _b.sent(),
_a.entity = entity,
_a.entityId = ele.id,
_a.entity = entity,
_a)];
}
});
}); }))];
case 17:
case 16:
_b = (_h.data = _m.sent(),
_h);
return [3 /*break*/, 20];
case 18:
return [3 /*break*/, 19];
case 17:
_j = {
id: 'dummy',
action: 'create'
};
_k = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 19:
case 18:
_b = [(_j.data = (_k.id = _m.sent(),
_k.entity = entity,
_k.entityId = data.id,
_k.entity = entity,
_k),
_j)];
_m.label = 20;
case 20:
_m.label = 19;
case 19:
createOper = (_f.data = (_g.operEntity$oper = _b,
_g),
_f);
@ -989,13 +1244,13 @@ var CascadeStore = /** @class */ (function (_super) {
dontCollect: true,
dontCreateOper: true,
})];
case 21:
case 20:
_m.sent();
_m.label = 22;
case 22: return [2 /*return*/, result_1];
case 23:
_m.label = 21;
case 21: return [2 /*return*/, result_1];
case 22:
ids_1 = (0, filter_2.getRelevantIds)(filter);
if (!(ids_1.length === 0)) return [3 /*break*/, 25];
if (!(ids_1.length === 0)) return [3 /*break*/, 24];
selection = {
data: {
id: 1,
@ -1007,17 +1262,17 @@ var CascadeStore = /** @class */ (function (_super) {
return [4 /*yield*/, this.selectAbjointRowAsync(entity, selection, context, {
dontCollect: true,
})];
case 24:
case 23:
rows = _m.sent();
ids_1.push.apply(ids_1, tslib_1.__spreadArray([], tslib_1.__read((rows.map(function (ele) { return ele.id; }))), false));
_m.label = 25;
case 25:
_m.label = 24;
case 24:
if (data) {
this.preProcessDataUpdated(data);
}
if (!(option.modiParentEntity && !['modi', 'modiEntity'].includes(entity))) return [3 /*break*/, 31];
if (!(option.modiParentEntity && !['modi', 'modiEntity'].includes(entity))) return [3 /*break*/, 30];
modiUpsert = void 0;
if (!(action !== 'remove')) return [3 /*break*/, 27];
if (!(action !== 'remove')) return [3 /*break*/, 26];
return [4 /*yield*/, this.selectAbjointRowAsync('modi', {
data: {
id: 1,
@ -1048,7 +1303,7 @@ var CascadeStore = /** @class */ (function (_super) {
indexFrom: 0,
count: 1,
}, context, option)];
case 26:
case 25:
upsertModis = _m.sent();
if (upsertModis.length > 0) {
_c = upsertModis[0], originData = _c.data, originId = _c.id;
@ -1063,9 +1318,9 @@ var CascadeStore = /** @class */ (function (_super) {
}
};
}
_m.label = 27;
case 27:
if (!!modiUpsert) return [3 /*break*/, 29];
_m.label = 26;
case 26:
if (!!modiUpsert) return [3 /*break*/, 28];
modiUpsert = {
id: 'dummy',
action: 'create',
@ -1080,7 +1335,7 @@ var CascadeStore = /** @class */ (function (_super) {
filter: filter,
},
};
if (!(ids_1.length > 0)) return [3 /*break*/, 29];
if (!(ids_1.length > 0)) return [3 /*break*/, 28];
_d = modiUpsert.data;
_l = {
id: 'dummy',
@ -1100,15 +1355,15 @@ var CascadeStore = /** @class */ (function (_super) {
}
});
}); }))];
case 28:
case 27:
_d.modiEntity$modi = (_l.data = _m.sent(),
_l);
_m.label = 29;
case 29: return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiUpsert, context, option)];
case 30:
_m.label = 28;
case 28: return [4 /*yield*/, this.cascadeUpdateAsync('modi', modiUpsert, context, option)];
case 29:
_m.sent();
return [2 /*return*/, 1];
case 31:
case 30:
createOper = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var createOper_1;
var _a, _b, _c;
@ -1126,7 +1381,8 @@ var CascadeStore = /** @class */ (function (_super) {
_b = {
id: operId,
action: action,
data: data
data: data,
targetEntity: entity
};
_c = {
id: 'dummy',
@ -1140,8 +1396,8 @@ var CascadeStore = /** @class */ (function (_super) {
_a = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 1: return [2 /*return*/, (_a.id = _b.sent(),
_a.entity = entity,
_a.entityId = ele,
_a.entity = entity,
_a)];
}
});
@ -1162,7 +1418,7 @@ var CascadeStore = /** @class */ (function (_super) {
}
});
}); };
if (!(action === 'remove')) return [3 /*break*/, 32];
if (!(action === 'remove')) return [3 /*break*/, 31];
if (!option.dontCollect) {
context.opRecords.push({
a: 'r',
@ -1174,10 +1430,10 @@ var CascadeStore = /** @class */ (function (_super) {
},
});
}
return [3 /*break*/, 36];
case 32:
return [3 /*break*/, 35];
case 31:
updateAttrCount = Object.keys(data).length;
if (!(updateAttrCount > 0)) return [3 /*break*/, 33];
if (!(updateAttrCount > 0)) return [3 /*break*/, 32];
// 优化一下,如果不更新任何属性,则不实际执行
Object.assign(data, {
$$updateAt$$: now,
@ -1194,21 +1450,21 @@ var CascadeStore = /** @class */ (function (_super) {
},
});
}
return [3 /*break*/, 36];
case 33:
if (!(action !== 'update')) return [3 /*break*/, 35];
return [3 /*break*/, 35];
case 32:
if (!(action !== 'update')) return [3 /*break*/, 34];
// 如果不是update动作而是用户自定义的动作这里还是要记录oper
return [4 /*yield*/, createOper()];
case 34:
case 33:
// 如果不是update动作而是用户自定义的动作这里还是要记录oper
_m.sent();
return [2 /*return*/, 0];
case 35: return [2 /*return*/, 0];
case 36: return [4 /*yield*/, this.updateAbjointRowAsync(entity, operation, context, option)];
case 37:
case 34: return [2 /*return*/, 0];
case 35: return [4 /*yield*/, this.updateAbjointRowAsync(entity, operation, context, option)];
case 36:
result = _m.sent();
return [4 /*yield*/, createOper()];
case 38:
case 37:
_m.sent();
return [2 /*return*/, result];
}
@ -1216,7 +1472,7 @@ var CascadeStore = /** @class */ (function (_super) {
});
};
CascadeStore.prototype.doUpdateSingleRow = function (entity, operation, context, option) {
var e_4, _a;
var e_5, _a;
var _this = this;
var data = operation.data, action = operation.action, operId = operation.id, filter = operation.filter;
var now = Date.now();
@ -1239,8 +1495,8 @@ var CascadeStore = /** @class */ (function (_super) {
}
else {
try {
for (var data_2 = tslib_1.__values(data), data_2_1 = data_2.next(); !data_2_1.done; data_2_1 = data_2.next()) {
var d = data_2_1.value;
for (var data_3 = tslib_1.__values(data), data_3_1 = data_3.next(); !data_3_1.done; data_3_1 = data_3.next()) {
var d = data_3_1.value;
var createSingleOper = {
id: 'any',
action: 'create',
@ -1249,12 +1505,12 @@ var CascadeStore = /** @class */ (function (_super) {
createInner(createSingleOper);
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (data_2_1 && !data_2_1.done && (_a = data_2.return)) _a.call(data_2);
if (data_3_1 && !data_3_1.done && (_a = data_3.return)) _a.call(data_3);
}
finally { if (e_4) throw e_4.error; }
finally { if (e_5) throw e_5.error; }
}
}
}
@ -1284,8 +1540,8 @@ var CascadeStore = /** @class */ (function (_super) {
}
};
CascadeStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
var e_5, _a, e_6, _b, e_7, _c;
(0, selection_1.reinforceOperation)(this.getSchema(), entity, operation);
var e_6, _a, e_7, _b, e_8, _c;
this.reinforceOperation(entity, operation);
var action = operation.action, data = operation.data, filter = operation.filter, id = operation.id;
var opData;
var wholeBeforeFns = [];
@ -1294,20 +1550,20 @@ var CascadeStore = /** @class */ (function (_super) {
if (['create', 'create-l'].includes(action) && data instanceof Array) {
opData = [];
try {
for (var data_3 = tslib_1.__values(data), data_3_1 = data_3.next(); !data_3_1.done; data_3_1 = data_3.next()) {
var d = data_3_1.value;
for (var data_4 = tslib_1.__values(data), data_4_1 = data_4.next(); !data_4_1.done; data_4_1 = data_4.next()) {
var d = data_4_1.value;
var _d = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdate), od = _d.data, beforeFns = _d.beforeFns, afterFns = _d.afterFns;
opData.push(od);
wholeBeforeFns.push.apply(wholeBeforeFns, tslib_1.__spreadArray([], tslib_1.__read(beforeFns), false));
wholeAfterFns.push.apply(wholeAfterFns, tslib_1.__spreadArray([], tslib_1.__read(afterFns), false));
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (data_3_1 && !data_3_1.done && (_a = data_3.return)) _a.call(data_3);
if (data_4_1 && !data_4_1.done && (_a = data_4.return)) _a.call(data_4);
}
finally { if (e_5) throw e_5.error; }
finally { if (e_6) throw e_6.error; }
}
}
else {
@ -1325,12 +1581,12 @@ var CascadeStore = /** @class */ (function (_super) {
before_1();
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (wholeBeforeFns_1_1 && !wholeBeforeFns_1_1.done && (_b = wholeBeforeFns_1.return)) _b.call(wholeBeforeFns_1);
}
finally { if (e_6) throw e_6.error; }
finally { if (e_7) throw e_7.error; }
}
var count = this.doUpdateSingleRow(entity, operation2, context, option);
try {
@ -1339,12 +1595,12 @@ var CascadeStore = /** @class */ (function (_super) {
after_1();
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (wholeAfterFns_1_1 && !wholeAfterFns_1_1.done && (_c = wholeAfterFns_1.return)) _c.call(wholeAfterFns_1);
}
finally { if (e_7) throw e_7.error; }
finally { if (e_8) throw e_8.error; }
}
return result;
};
@ -1357,12 +1613,12 @@ var CascadeStore = /** @class */ (function (_super) {
*/
CascadeStore.prototype.cascadeUpdateAsync = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var action, data, filter, id, opData, wholeBeforeFns, wholeAfterFns, result, data_4, data_4_1, d, _a, od, beforeFns, afterFns, _b, od, beforeFns, afterFns, operation2, wholeBeforeFns_2, wholeBeforeFns_2_1, before_2, e_8_1, count, wholeAfterFns_2, wholeAfterFns_2_1, after_2, e_9_1;
var e_10, _c, e_8, _d, _e, _f, e_9, _g;
var action, data, filter, id, opData, wholeBeforeFns, wholeAfterFns, result, data_5, data_5_1, d, _a, od, beforeFns, afterFns, _b, od, beforeFns, afterFns, operation2, wholeBeforeFns_2, wholeBeforeFns_2_1, before_2, e_9_1, count, wholeAfterFns_2, wholeAfterFns_2_1, after_2, e_10_1;
var e_11, _c, e_9, _d, _e, _f, e_10, _g;
return tslib_1.__generator(this, function (_h) {
switch (_h.label) {
case 0:
(0, selection_1.reinforceOperation)(this.getSchema(), entity, operation);
this.reinforceOperation(entity, operation);
action = operation.action, data = operation.data, filter = operation.filter, id = operation.id;
wholeBeforeFns = [];
wholeAfterFns = [];
@ -1370,20 +1626,20 @@ var CascadeStore = /** @class */ (function (_super) {
if (['create', 'create-l'].includes(action) && data instanceof Array) {
opData = [];
try {
for (data_4 = tslib_1.__values(data), data_4_1 = data_4.next(); !data_4_1.done; data_4_1 = data_4.next()) {
d = data_4_1.value;
for (data_5 = tslib_1.__values(data), data_5_1 = data_5.next(); !data_5_1.done; data_5_1 = data_5.next()) {
d = data_5_1.value;
_a = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdateAsync), od = _a.data, beforeFns = _a.beforeFns, afterFns = _a.afterFns;
opData.push(od);
wholeBeforeFns.push.apply(wholeBeforeFns, tslib_1.__spreadArray([], tslib_1.__read(beforeFns), false));
wholeAfterFns.push.apply(wholeAfterFns, tslib_1.__spreadArray([], tslib_1.__read(afterFns), false));
}
}
catch (e_10_1) { e_10 = { error: e_10_1 }; }
catch (e_11_1) { e_11 = { error: e_11_1 }; }
finally {
try {
if (data_4_1 && !data_4_1.done && (_c = data_4.return)) _c.call(data_4);
if (data_5_1 && !data_5_1.done && (_c = data_5.return)) _c.call(data_5);
}
finally { if (e_10) throw e_10.error; }
finally { if (e_11) throw e_11.error; }
}
}
else {
@ -1412,14 +1668,14 @@ var CascadeStore = /** @class */ (function (_super) {
return [3 /*break*/, 2];
case 5: return [3 /*break*/, 8];
case 6:
e_8_1 = _h.sent();
e_8 = { error: e_8_1 };
e_9_1 = _h.sent();
e_9 = { error: e_9_1 };
return [3 /*break*/, 8];
case 7:
try {
if (wholeBeforeFns_2_1 && !wholeBeforeFns_2_1.done && (_d = wholeBeforeFns_2.return)) _d.call(wholeBeforeFns_2);
}
finally { if (e_8) throw e_8.error; }
finally { if (e_9) throw e_9.error; }
return [7 /*endfinally*/];
case 8: return [4 /*yield*/, this.doUpdateSingleRowAsync(entity, operation2, context, option)];
case 9:
@ -1446,14 +1702,14 @@ var CascadeStore = /** @class */ (function (_super) {
return [3 /*break*/, 11];
case 14: return [3 /*break*/, 17];
case 15:
e_9_1 = _h.sent();
e_9 = { error: e_9_1 };
e_10_1 = _h.sent();
e_10 = { error: e_10_1 };
return [3 /*break*/, 17];
case 16:
try {
if (wholeAfterFns_2_1 && !wholeAfterFns_2_1.done && (_g = wholeAfterFns_2.return)) _g.call(wholeAfterFns_2);
}
finally { if (e_9) throw e_9.error; }
finally { if (e_10) throw e_10.error; }
return [7 /*endfinally*/];
case 17: return [2 /*return*/, result];
}
@ -1461,7 +1717,7 @@ var CascadeStore = /** @class */ (function (_super) {
});
};
CascadeStore.prototype.cascadeSelect = function (entity, selection, context, option) {
(0, selection_1.reinforceSelection)(this.getSchema(), entity, selection);
this.reinforceSelection(entity, selection);
var data = selection.data, filter = selection.filter, indexFrom = selection.indexFrom, count = selection.count, sorter = selection.sorter;
var _a = this.destructCascadeSelect(entity, data, context, this.cascadeSelect, this.aggregateSync, option), projection = _a.projection, cascadeSelectionFns = _a.cascadeSelectionFns;
var rows = this.selectAbjointRow(entity, {
@ -1544,11 +1800,11 @@ var CascadeStore = /** @class */ (function (_super) {
var id = row.id;
if (!entityBranch_1[id]) {
Object.assign(entityBranch_1, (_a = {},
_a[id] = row,
_a[id] = (0, lodash_1.cloneDeep)(row),
_a));
}
else {
Object.assign(entityBranch_1[id], row);
Object.assign(entityBranch_1[id], (0, lodash_1.cloneDeep)(row));
}
}
});
@ -1568,7 +1824,7 @@ var CascadeStore = /** @class */ (function (_super) {
if (row) {
var id = row.id;
Object.assign(entityBranch, (_a = {},
_a[id] = row,
_a[id] = (0, lodash_1.cloneDeep)(row),
_a));
}
});
@ -1583,7 +1839,7 @@ var CascadeStore = /** @class */ (function (_super) {
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
(0, selection_1.reinforceSelection)(this.getSchema(), entity, selection);
this.reinforceSelection(entity, selection);
data = selection.data, filter = selection.filter, indexFrom = selection.indexFrom, count = selection.count, sorter = selection.sorter;
_a = this.destructCascadeSelect(entity, data, context, this.cascadeSelectAsync, this.aggregateAsync, option), projection = _a.projection, cascadeSelectionFns = _a.cascadeSelectionFns;
return [4 /*yield*/, this.selectAbjointRowAsync(entity, {
@ -1601,7 +1857,7 @@ var CascadeStore = /** @class */ (function (_super) {
if (!(cascadeSelectionFns.length > 0)) return [3 /*break*/, 3];
ruException_2 = [];
return [4 /*yield*/, Promise.all(cascadeSelectionFns.map(function (ele) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var e_11, rows_2;
var e_12, rows_2;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
@ -1611,13 +1867,13 @@ var CascadeStore = /** @class */ (function (_super) {
_a.sent();
return [3 /*break*/, 3];
case 2:
e_11 = _a.sent();
if (e_11 instanceof types_1.OakRowUnexistedException) {
rows_2 = e_11.getRows();
e_12 = _a.sent();
if (e_12 instanceof types_1.OakRowUnexistedException) {
rows_2 = e_12.getRows();
ruException_2.push.apply(ruException_2, tslib_1.__spreadArray([], tslib_1.__read(rows_2), false));
}
else {
throw e_11;
throw e_12;
}
return [3 /*break*/, 3];
case 3: return [2 /*return*/];

View File

@ -1,6 +1,6 @@
import { EntityDict, RowStore, OperateOption, OperationResult, SelectOption, TxnOption, Context, AggregationResult } from "../types";
export declare abstract class SyncContext<ED extends EntityDict> implements Context {
private rowStore;
rowStore: SyncRowStore<ED, this>;
private uuid?;
constructor(rowStore: SyncRowStore<ED, SyncContext<ED>>);
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;

View File

@ -33,7 +33,7 @@ var TriggerExecutor = /** @class */ (function () {
TriggerExecutor.prototype.registerChecker = function (checker) {
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker, true), fn = _a.fn, when = _a.when;
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
var priority = type === 'data' ? Trigger_1.DATA_CHECKER_DEFAULT_PRIORITY : Trigger_1.CHECKER_DEFAULT_PRIORITY; // checker的默认优先级最低前面的trigger可能会赋上一些相应的值
var trigger = {
checkerType: type,

View File

@ -8,7 +8,7 @@ import { SyncContext } from './SyncRowStore';
* @param silent silentrow和relation类型的checker只会把限制条件加到查询上create动作
* @returns
*/
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>, silent?: boolean): {
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
fn: Trigger<ED, T, Cxt>['fn'];
when: 'before' | 'after';
};

View File

@ -17,7 +17,7 @@ var uuid_1 = require("../utils/uuid");
* @param silent 如果silent则row和relation类型的checker只会把限制条件加到查询上而不报错除掉create动作
* @returns
*/
function translateCheckerInAsyncContext(checker, silent) {
function translateCheckerInAsyncContext(checker) {
var _this = this;
var entity = checker.entity, type = checker.type;
var when = 'before'; // 现在create的relation改成提前的expression检查了原先是先插入再后检查性能不行而且select也需要实现前检查
@ -65,7 +65,7 @@ function translateCheckerInAsyncContext(checker, silent) {
_c.label = 3;
case 3:
filter2 = _b;
if (!silent) return [3 /*break*/, 4];
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 4];
operation.filter = (0, filter_1.addFilterSegment)(operationFilter || {}, filter2);
return [2 /*return*/, 0];
case 4: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)];
@ -135,7 +135,7 @@ function translateCheckerInAsyncContext(checker, silent) {
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
return [2 /*return*/, 0];
}
if (silent) {
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = (0, filter_1.addFilterSegment)(filter || {}, result);
return [2 /*return*/, 0];
}
@ -409,7 +409,7 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) {
counters.push.apply(counters, tslib_1.__spreadArray([], tslib_1.__read(ca2), false));
}
if (filter.$or) {
var countersOr = filter.$and.map(function (ele) { return translateCreateFilterMaker(entity, ele, userId); });
var countersOr = filter.$or.map(function (ele) { return translateCreateFilterMaker(entity, ele, userId); });
// or也只要有一个满足就行不能否定
var co2 = countersOr.filter(function (ele) { return !(ele instanceof Exception_1.OakUserUnpermittedException); });
counters.push.apply(counters, tslib_1.__spreadArray([], tslib_1.__read(co2), false));

View File

@ -1,19 +0,0 @@
import { StorageSchema } from '../types';
import { EntityDict } from '../types/Entity';
declare type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
export declare function registerSelectionRewriter<ED extends EntityDict>(rewriter: SelectionRewriter<ED>): void;
declare type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
export declare function registerOperationRewriter<ED extends EntityDict>(rewriter: OperationRewriter<ED>): void;
/**
* selection进行一些完善
* @param selection
*/
export declare function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']): void;
/**
* operation进行一些完善operation算子的注入点
* @param schema
* @param entity
* @param selection
*/
export declare function reinforceOperation<ED extends EntityDict>(schema: StorageSchema<ED>, entity: keyof ED, operation: ED[keyof ED]['Operation']): void;
export {};

View File

@ -1,265 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reinforceOperation = exports.reinforceSelection = exports.registerOperationRewriter = exports.registerSelectionRewriter = void 0;
var tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert"));
var types_1 = require("../types");
var Demand_1 = require("../types/Demand");
var filter_1 = require("./filter");
var relation_1 = require("./relation");
var SelectionRewriters = [];
function registerSelectionRewriter(rewriter) {
SelectionRewriters.push(rewriter);
}
exports.registerSelectionRewriter = registerSelectionRewriter;
function getSelectionRewriters() {
return SelectionRewriters;
}
var OperationRewriters = [];
function registerOperationRewriter(rewriter) {
OperationRewriters.push(rewriter);
}
exports.registerOperationRewriter = registerOperationRewriter;
function getOperationRewriters() {
return OperationRewriters;
}
/**
* 对selection进行一些完善避免编程人员的疏漏
* @param selection
*/
function reinforceSelection(schema, entity, selection) {
var filter = selection.filter, data = selection.data, sorter = selection.sorter;
var checkNode = function (projectionNode, attrs) {
attrs.forEach(function (attr) {
var _a;
if (!projectionNode.hasOwnProperty(attr)) {
Object.assign(projectionNode, (_a = {},
_a[attr] = 1,
_a));
}
});
};
var relevantIds = [];
if (filter) {
var toBeAssignNode_1 = {}; // 用来记录在表达式中涉及到的结点
// filter当中所关联到的属性必须在projection中
var filterNodeDict_1 = {};
var checkFilterNode_1 = function (entity2, filterNode, projectionNode) {
var _a, e_1, _b, _c, _d, _e, _f;
var necessaryAttrs = ['id'];
for (var attr in filterNode) {
if (attr === '#id') {
(0, assert_1.default)(!filterNodeDict_1[filterNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(filterNode[attr]));
Object.assign(filterNodeDict_1, (_a = {},
_a[filterNode[attr]] = projectionNode,
_a));
if (toBeAssignNode_1[filterNode[attr]]) {
checkNode(projectionNode, toBeAssignNode_1[filterNode[attr]]);
}
}
else if (['$and', '$or'].includes(attr)) {
try {
for (var _g = (e_1 = void 0, tslib_1.__values(filterNode[attr])), _h = _g.next(); !_h.done; _h = _g.next()) {
var node = _h.value;
checkFilterNode_1(entity2, node, projectionNode);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
}
finally { if (e_1) throw e_1.error; }
}
}
else if (attr === '$not') {
checkFilterNode_1(entity2, filterNode[attr], projectionNode);
}
else if (attr === '$text') {
// 全文检索首先要有fulltext索引其次要把fulltext的相关属性加到projection里
var indexes = schema[entity2].indexes;
var fulltextIndex = indexes.find(function (ele) { return ele.config && ele.config.type === 'fulltext'; });
var attributes = fulltextIndex.attributes;
necessaryAttrs.push.apply(necessaryAttrs, tslib_1.__spreadArray([], tslib_1.__read((attributes.map(function (ele) { return ele.name; }))), false));
}
else {
if (attr.toLowerCase().startsWith(Demand_1.EXPRESSION_PREFIX)) {
var exprResult = (0, types_1.getAttrRefInExpression)(filterNode[attr]);
for (var nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (filterNodeDict_1[nodeName]) {
checkNode(filterNodeDict_1[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode_1[nodeName]) {
(_c = toBeAssignNode_1[nodeName]).push.apply(_c, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
}
else {
Object.assign(toBeAssignNode_1, (_d = {},
_d[nodeName] = exprResult[nodeName],
_d));
}
}
}
}
else {
var rel = (0, relation_1.judgeRelation)(schema, entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
if (!projectionNode[attr]) {
Object.assign(projectionNode, (_e = {},
_e[attr] = {
id: 1,
},
_e));
}
checkFilterNode_1(attr, filterNode[attr], projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push("".concat(attr, "Id"));
if (!projectionNode[attr]) {
Object.assign(projectionNode, (_f = {},
_f[attr] = {
id: 1,
},
_f));
}
checkFilterNode_1(rel, filterNode[attr], projectionNode[attr]);
}
else if (rel instanceof Array) {
// 现在filter中还不支持一对多的语义先放着吧
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
};
checkFilterNode_1(entity, filter, data);
relevantIds = (0, filter_1.getRelevantIds)(filter);
}
// sorter感觉现在取不取影响不大前端的list直接获取返回的ids了先不管之
if (sorter) {
}
var toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
var projectionNodeDict = {};
var checkProjectionNode = function (entity2, projectionNode) {
var _a, _b, _c;
var necessaryAttrs = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据有时两个页面的filter的差异会导致有一个加createAt有一个不加此时可能产生前台取数据不完整的异常。先统一加上
for (var attr in projectionNode) {
if (attr === '#id') {
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
Object.assign(projectionNodeDict, (_a = {},
_a[projectionNode[attr]] = projectionNode,
_a));
if (toBeAssignNode2[projectionNode[attr]]) {
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]]);
}
}
else {
if (attr.toLowerCase().startsWith(Demand_1.EXPRESSION_PREFIX)) {
var exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
for (var nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (projectionNodeDict[nodeName]) {
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode2[nodeName]) {
(_b = toBeAssignNode2[nodeName]).push.apply(_b, tslib_1.__spreadArray([], tslib_1.__read(exprResult[nodeName]), false));
}
else {
Object.assign(toBeAssignNode2, (_c = {},
_c[nodeName] = exprResult[nodeName],
_c));
}
}
}
}
else {
var rel = (0, relation_1.judgeRelation)(schema, entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
checkProjectionNode(attr, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push("".concat(attr, "Id"));
checkProjectionNode(rel, projectionNode[attr]);
}
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
var data_1 = projectionNode[attr].data;
if (rel[1]) {
checkNode(data_1, [rel[1]]);
}
else {
checkNode(data_1, ['entity', 'entityId']);
}
reinforceSelection(schema, rel[0], projectionNode[attr]);
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
// 如果对象中指向一对多的Modi此时加上指向Modi的projection
if (schema[entity2].toModi) {
Object.assign(projectionNode, {
modi$entity: {
$entity: 'modi',
data: {
id: 1,
targetEntity: 1,
entity: 1,
entityId: 1,
action: 1,
iState: 1,
data: 1,
filter: 1,
},
filter: {
iState: 'active',
},
}
});
}
};
checkProjectionNode(entity, data);
if (!sorter && relevantIds.length === 0) {
// 如果没有sorter就给予一个按createAt逆序的sorter
Object.assign(selection, {
sorter: [
{
$attr: {
$$createAt$$: 1,
},
$direction: 'desc',
}
]
});
Object.assign(data, {
$$createAt$$: 1,
});
}
SelectionRewriters.forEach(function (ele) { return ele(schema, entity, selection); });
}
exports.reinforceSelection = reinforceSelection;
/**
* 对operation进行一些完善作为operation算子的注入点
* @param schema
* @param entity
* @param selection
*/
function reinforceOperation(schema, entity, operation) {
OperationRewriters.forEach(function (ele) { return ele(schema, entity, operation); });
}
exports.reinforceOperation = reinforceOperation;

18
lib/timers/oper.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
import { EntityDict } from '../types/Entity';
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
export declare type VaccumOperOption<ED extends EntityDict & BaseEntityDict> = {
aliveLine: number;
excludeOpers?: {
[T in keyof ED]?: ED[T]['Action'][];
};
backupDir?: string;
zip?: boolean;
};
/**
* oper对象清空
* @param option
* @param context
* @returns
*/
export declare function vaccumOper<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOperOption<ED>, context: Cxt): Promise<void>;

60
lib/timers/oper.js Normal file
View File

@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vaccumOper = void 0;
var tslib_1 = require("tslib");
var vaccum_1 = require("./vaccum");
var filter_1 = require("../store/filter");
/**
* 将一定日期之前的oper对象清空
* @param option
* @param context
* @returns
*/
function vaccumOper(option, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var aliveLine, excludeOpers, rest, operFilter, notFilters, key;
return tslib_1.__generator(this, function (_a) {
aliveLine = option.aliveLine, excludeOpers = option.excludeOpers, rest = tslib_1.__rest(option, ["aliveLine", "excludeOpers"]);
operFilter = {};
if (excludeOpers) {
notFilters = [];
for (key in excludeOpers) {
if (excludeOpers[key].length > 0) {
notFilters.push({
targetEntity: key,
action: {
$in: excludeOpers[key],
}
});
}
else {
notFilters.push({
targetEntity: key,
});
}
}
if (notFilters.length > 0) {
operFilter.$not = {
$or: notFilters,
};
}
}
return [2 /*return*/, (0, vaccum_1.vaccumEntities)(tslib_1.__assign({ entities: [{
entity: 'operEntity',
aliveLine: aliveLine + 10000,
filter: {
oper: (0, filter_1.combineFilters)([operFilter, {
$$createAt$$: {
$lt: aliveLine,
}
}]),
},
}, {
entity: 'oper',
aliveLine: aliveLine,
filter: operFilter,
}] }, rest), context)];
});
});
}
exports.vaccumOper = vaccumOper;

20
lib/timers/vaccum.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
import { EntityDict } from '../types/Entity';
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
declare type VaccumOptionEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
entity: T;
filter?: ED[T]['Selection']['filter'];
aliveLine: number;
};
declare type VaccumOption<ED extends EntityDict & BaseEntityDict> = {
entities: Array<VaccumOptionEntity<ED, keyof ED>>;
backupDir?: string;
zip?: boolean;
};
/**
*
*
* @param option
*/
export declare function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOption<ED>, context: Cxt): Promise<void>;
export {};

176
lib/timers/vaccum.js Normal file
View File

@ -0,0 +1,176 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vaccumEntities = void 0;
var tslib_1 = require("tslib");
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
var fs_1 = require("fs");
var filter_1 = require("../store/filter");
var node_zlib_1 = require("node:zlib");
var stream_1 = require("stream");
var uuid_1 = require("../utils/uuid");
/**
* 删除数据库中的部分数据减少体积
* 一般只删除日志类数据
* @param option
*/
function vaccumEntities(option, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var entities, backupDir, _loop_1, entities_1, entities_1_1, ele, e_1_1;
var e_1, _a;
var _this = this;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
entities = option.entities, backupDir = option.backupDir;
_loop_1 = function (ele) {
var entity, filter, aliveLine, filter2, zip, now, backFile, fd_1, attributes_1, projection_1, attr, count_1, appendData_1, gzip_1, source_1, destination_1, _c, _d, _e;
var _f, _g;
return tslib_1.__generator(this, function (_h) {
switch (_h.label) {
case 0:
entity = ele.entity, filter = ele.filter, aliveLine = ele.aliveLine;
filter2 = {
$$createAt$$: {
$lt: aliveLine,
},
};
if (filter) {
filter2 = (0, filter_1.combineFilters)([filter2, filter]);
}
if (!backupDir) return [3 /*break*/, 4];
zip = option.zip;
now = (0, dayjs_1.default)();
backFile = "".concat(backupDir, "/").concat(entity, "-").concat(now.format('YYYY-MM-DD HH:mm:ss'), ".csv");
if ((0, fs_1.existsSync)(backFile)) {
(0, fs_1.rmSync)(backFile);
}
fd_1 = (0, fs_1.openSync)(backFile, 'a');
attributes_1 = ['id', '$$createAt$$', '$$updateAt$$', '$$deleteAt$$'];
projection_1 = {
id: 1,
$$createAt$$: 1,
$$updateAt$$: 1,
$$deleteAt$$: 1,
};
for (attr in context.getSchema()[entity].attributes) {
Object.assign(projection_1, (_f = {},
_f[attr] = 1,
_f));
attributes_1.push(attr);
}
(0, fs_1.appendFileSync)(fd_1, attributes_1.join(','));
(0, fs_1.appendFileSync)(fd_1, '\n');
count_1 = 0;
appendData_1 = function (minCreateAt) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var filter3, rows, csvTxt, maxCreateAt;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
filter3 = (0, filter_1.combineFilters)([filter2, {
$$createAt$$: {
$gt: minCreateAt,
},
}]);
return [4 /*yield*/, context.select(entity, {
data: projection_1,
filter: filter3,
sorter: [{
$attr: {
$$createAt$$: 1,
},
$direction: 'asc'
}],
indexFrom: 0,
count: 1000,
}, { includedDeleted: true })];
case 1:
rows = _a.sent();
csvTxt = rows.map(function (row) { return attributes_1.map(function (attr) { return JSON.stringify(row[attr]); }).join(','); }).join('\n');
(0, fs_1.appendFileSync)(fd_1, csvTxt);
(0, fs_1.appendFileSync)(fd_1, '\n');
count_1 += rows.length;
if (rows.length === 1000) {
maxCreateAt = rows[999].$$createAt$$;
return [2 /*return*/, appendData_1(maxCreateAt)];
}
return [2 /*return*/];
}
});
}); };
return [4 /*yield*/, appendData_1(0)];
case 1:
_h.sent();
(0, fs_1.closeSync)(fd_1);
console.log("\u5907\u4EFD".concat(entity, "\u5BF9\u8C61\u5B8C\u6BD5\uFF0C\u5171\u5907\u4EFD\u4E86").concat(count_1, "\u884C\u6570\u636E"));
if (!(count_1 === 0)) return [3 /*break*/, 2];
(0, fs_1.rmSync)(backFile);
return [3 /*break*/, 4];
case 2:
if (!zip) return [3 /*break*/, 4];
gzip_1 = (0, node_zlib_1.createGzip)();
source_1 = (0, fs_1.createReadStream)(backFile);
destination_1 = (0, fs_1.createWriteStream)("".concat(backFile, ".zip"));
return [4 /*yield*/, new Promise(function (resolve, reject) {
(0, stream_1.pipeline)(source_1, gzip_1, destination_1, function (err) {
if (err) {
reject(err);
}
else {
resolve(undefined);
}
});
})];
case 3:
_h.sent();
_h.label = 4;
case 4:
_d = (_c = context).operate;
_e = [entity];
_g = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 5:
// 将对应的数据删除
return [4 /*yield*/, _d.apply(_c, _e.concat([(_g.id = _h.sent(),
_g.action = 'remove',
_g.data = {},
_g.filter = filter2,
_g), { deletePhysically: true }]))];
case 6:
// 将对应的数据删除
_h.sent();
return [2 /*return*/];
}
});
};
_b.label = 1;
case 1:
_b.trys.push([1, 6, 7, 8]);
entities_1 = tslib_1.__values(entities), entities_1_1 = entities_1.next();
_b.label = 2;
case 2:
if (!!entities_1_1.done) return [3 /*break*/, 5];
ele = entities_1_1.value;
return [5 /*yield**/, _loop_1(ele)];
case 3:
_b.sent();
_b.label = 4;
case 4:
entities_1_1 = entities_1.next();
return [3 /*break*/, 2];
case 5: return [3 /*break*/, 8];
case 6:
e_1_1 = _b.sent();
e_1 = { error: e_1_1 };
return [3 /*break*/, 8];
case 7:
try {
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
}
finally { if (e_1) throw e_1.error; }
return [7 /*endfinally*/];
case 8: return [2 /*return*/];
}
});
});
}
exports.vaccumEntities = vaccumEntities;

View File

@ -16,10 +16,10 @@ export declare abstract class Connector<ED extends EntityDict, BackCxt extends A
params: any;
context: BackCxt;
}>;
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): {
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): Promise<{
body: any;
headers?: Record<string, any>;
};
}>;
abstract serializeException(exception: OakException<ED>, headers: IncomingHttpHeaders, body: any): {
body: any;
headers?: Record<string, any>;

View File

@ -37,6 +37,7 @@ export declare type OperateOption = {
allowExists?: boolean;
modiParentId?: string;
modiParentEntity?: string;
deletePhysically?: boolean;
dummy?: 1;
};
export declare type FormUpdateData<SH extends GeneralEntityShape> = Partial<{

View File

@ -3,9 +3,13 @@ import { StorageSchema } from './Storage';
export declare type TxnOption = {
isolationLevel: 'repeatable read' | 'serializable';
};
export declare type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
export declare type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
export declare abstract class RowStore<ED extends EntityDict> {
protected storageSchema: StorageSchema<ED>;
constructor(storageSchema: StorageSchema<ED>);
abstract registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
getSchema(): StorageSchema<ED>;
mergeOperationResult(result: OperationResult<ED>, toBeMerged: OperationResult<ED>): void;
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;

View File

@ -24,10 +24,10 @@ export declare class SimpleConnector<ED extends EntityDict, BackCxt extends Asyn
params: any;
context: BackCxt;
}>;
serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): {
serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): Promise<{
body: any;
headers?: Record<string, any> | undefined;
};
}>;
serializeException(exception: OakException<ED>, headers: IncomingHttpHeaders, body: any): {
body: any;
headers?: Record<string, any> | undefined;

View File

@ -107,12 +107,19 @@ var SimpleConnector = /** @class */ (function (_super) {
});
};
SimpleConnector.prototype.serializeResult = function (result, context, headers, body) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (result instanceof stream_1.Stream || result instanceof Buffer) {
return {
return [2 /*return*/, {
body: result,
};
}];
}
return {
return [4 /*yield*/, context.refineOpRecords()];
case 1:
_a.sent();
return [2 /*return*/, {
body: {
result: result,
opRecords: context.opRecords,
@ -120,7 +127,10 @@ var SimpleConnector = /** @class */ (function (_super) {
headers: {
'oak-message': context.getMessage(),
},
};
}];
}
});
});
};
SimpleConnector.prototype.serializeException = function (exception, headers, body) {
return {

View File

@ -1,6 +1,6 @@
{
"name": "oak-domain",
"version": "2.6.6",
"version": "2.6.7",
"author": {
"name": "XuChang"
},

View File

@ -29,6 +29,7 @@ export const ACTION_CONSTANT_IN_OAK_DOMAIN = (level = 2) => {
export const RESERVED_ENTITIES = ['Schema', 'Filter', 'Query', 'SubQuery', 'Entity', 'Selection', 'Operation', 'File', 'Common',
'Locale', 'Projection', 'Data'];
export const ENTITY_NAME_MAX_LENGTH = 32;
export const STRING_LITERAL_MAX_LENGTH = 24;
export const NUMERICAL_LITERL_DEFAULT_PRECISION = 8;
export const NUMERICAL_LITERL_DEFAULT_SCALE = 2;

View File

@ -14,6 +14,7 @@ import {
NUMERICAL_LITERL_DEFAULT_SCALE,
INT_LITERL_DEFAULT_WIDTH,
LIB_OAK_DOMAIN,
ENTITY_NAME_MAX_LENGTH,
} from './env';
import { firstLetterLowerCase, firstLetterUpperCase } from '../utils/string';
@ -510,6 +511,19 @@ function getStringEnumValues(filename: string, program: ts.Program, obj: string,
}
}
function checkNameLegal(filename: string, attrName: string, upperCase?: boolean) {
assert(attrName.length <= ENTITY_NAME_MAX_LENGTH, `文件「${filename}」:「${attrName}」的名称定义过长,不能超过「${ENTITY_NAME_MAX_LENGTH}」长度`);
if (upperCase) {
assert(/[A-Z][a-z|A-Z|0-9]+/i.test(attrName), `文件「${filename}」:「${attrName}」的名称必须以大写字母开始,且只能包含字母和数字`);
}
else if (upperCase === false) {
assert(/[a-z][a-z|A-Z|0-9]+/i.test(attrName), `文件「${filename}」:「${attrName}」的名称必须以小写字母开始,且只能包含字母和数字`);
}
else {
assert(/[a-z|A-Z][a-z|A-Z|0-9]+/i.test(attrName), `文件「${filename}」:「${attrName}」的名称必须以字母开始,且只能包含字母和数字`);
}
}
function analyzeEntity(filename: string, path: string, program: ts.Program, relativePath?: string) {
const fullPath = `${path}/${filename}`;
const sourceFile = program.getSourceFile(fullPath);
@ -521,6 +535,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
// removeFromRelationShip(moduleName);
console.warn(`出现了同名的Entity定义「${moduleName}」,将使用${fullPath}取代掉默认对象,请检查新的对象结构及相关常量定义与原有的兼容,否则原有对象的相关逻辑会出现不可知异常`);
}
checkNameLegal(filename, moduleName, true);
const referencedSchemas: string[] = [];
const schemaAttrs: ts.TypeElement[] = [];
let hasFulltextIndex: boolean = false;
@ -587,6 +603,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
(attrNode) => {
const { type, name, questionToken } = <ts.PropertySignature>attrNode;
const attrName = (<ts.Identifier>name).text;
checkNameLegal(filename, attrName, false);
if (ts.isTypeReferenceNode(type!)
&& ts.isIdentifier(type.typeName)) {
if ((referencedSchemas.includes(type.typeName.text) || type.typeName.text === 'Schema')) {
@ -707,21 +724,13 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
[moduleName]: 1,
});
}
else if (hasEntityAttr || hasEntityIdAttr) {
throw new Error(`文件「${filename}」:属性 定义中只包含${hasEntityAttr ? 'entity' : 'entityId'}不符合定义规范。entity/entityId必须联合出现代表不定对象的反向指针`);
}
beforeSchema = false;
// 对于不是Oper的对象全部建立和OperEntity的反指关系
if (!['Oper', 'OperEntity', 'ModiEntity'].includes(moduleName)) {
if (ReversePointerRelations['OperEntity'] && !ReversePointerRelations['OperEntity'].includes(moduleName)) {
ReversePointerRelations['OperEntity'].push(moduleName);
}
else {
assign(ReversePointerRelations, {
['OperEntity']: [moduleName],
});
}
// 对于不是Modi的对象全部建立和ModiEntity的反指关系
if (!['Modi'].includes(moduleName) && !toModi) {
// 对于不是Modi和Oper的对象全部建立和ModiEntity的反指关系
if (!['Modi', 'Oper', 'OperEntity', 'ModiEntity'].includes(moduleName) && !toModi) {
if (ReversePointerRelations['ModiEntity'] && !ReversePointerRelations['ModiEntity'].includes(moduleName)) {
ReversePointerRelations['ModiEntity'].push(moduleName);
}
@ -730,8 +739,17 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
['ModiEntity']: [moduleName],
});
}
if (ReversePointerRelations['OperEntity'] && !ReversePointerRelations['OperEntity'].includes(moduleName)) {
ReversePointerRelations['OperEntity'].push(moduleName);
}
else {
assign(ReversePointerRelations, {
['OperEntity']: [moduleName],
});
}
}
}
else if (beforeSchema) {
// 本地规定的一些形状定义,直接使用
@ -5479,7 +5497,7 @@ function constructAttributes(entity: string): ts.PropertyAssignment[] {
if (ts.isUnionTypeNode(type!)) {
if (ts.isLiteralTypeNode(type.types[0])) {
if (ts.isStringLiteral(type.types[0].literal)) {
assert (enumAttributes && enumAttributes[(<ts.Identifier>name).text]);
assert(enumAttributes && enumAttributes[(<ts.Identifier>name).text]);
attrAssignments.push(
factory.createPropertyAssignment(
'type',

View File

@ -9,6 +9,7 @@ export interface Schema extends EntityShape {
filter?: Object;
extra?: Object;
operator?: User;
targetEntity: String<32>;
};
const configuration: Configuration = {
@ -24,6 +25,7 @@ const locale: LocaleDef<Schema, '', '', {}> = {
filter: '选择条件',
extra: '其它',
operator: '操作者',
targetEntity: '关联对象',
},
},
};

View File

@ -4,7 +4,7 @@ import assert from "assert";
import { IncomingHttpHeaders } from "http";
export abstract class AsyncContext<ED extends EntityDict> implements Context {
private rowStore: AsyncRowStore<ED, this>;
rowStore: AsyncRowStore<ED, this>;
private uuid?: string;
opRecords: OpRecord<ED>[];
private scene?: string;
@ -15,6 +15,11 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
rollback: Array<() => Promise<void>>;
}
/**
*
*/
abstract refineOpRecords(): Promise<void>;
constructor(store: AsyncRowStore<ED, AsyncContext<ED>>, headers?: IncomingHttpHeaders) {
this.rowStore = store;
this.opRecords = [];

View File

@ -4,19 +4,18 @@ import {
OperateOption, SelectOption, OperationResult, CreateAtAttribute, UpdateAtAttribute, AggregationResult, DeleteAtAttribute
} from "../types/Entity";
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { RowStore } from '../types/RowStore';
import { OperationRewriter, RowStore, SelectionRewriter } from '../types/RowStore';
import { StorageSchema } from '../types/Storage';
import { addFilterSegment, combineFilters } from "./filter";
import { judgeRelation } from "./relation";
import { OakRowUnexistedException } from "../types";
import { EXPRESSION_PREFIX, getAttrRefInExpression, OakRowUnexistedException } from "../types";
import { unset, uniq, cloneDeep, pick } from '../utils/lodash';
import { SyncContext } from "./SyncRowStore";
import { AsyncContext } from "./AsyncRowStore";
import { getRelevantIds } from "./filter";
import { CreateOperation as CreateOperOperation } from '../base-app-domain/Oper/Schema';
import { CreateSingleOperation as CreateSingleOperOperation } from '../base-app-domain/Oper/Schema';
import { CreateOperation as CreateModiOperation, UpdateOperation as UpdateModiOperation } from '../base-app-domain/Modi/Schema';
import { generateNewIdAsync } from "../utils/uuid";
import { reinforceOperation, reinforceSelection } from "./selection";
/**这个用来处理级联的select和update对不同能力的 */
export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> extends RowStore<ED> {
@ -26,6 +25,252 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
protected abstract supportManyToOneJoin(): boolean;
protected abstract supportMultipleCreate(): boolean;
private selectionRewriters: SelectionRewriter<any>[] = [];
private operationRewriters: OperationRewriter<any>[] = [];
private reinforceSelection(entity: keyof ED, selection: ED[keyof ED]['Selection']) {
const { filter, data, sorter } = selection;
const checkNode = (projectionNode: ED[keyof ED]['Selection']['data'], attrs: string[]) => {
attrs.forEach(
(attr) => {
if (!projectionNode.hasOwnProperty(attr)) {
Object.assign(projectionNode, {
[attr]: 1,
});
}
}
);
};
let relevantIds: string[] = [];
if (filter) {
const toBeAssignNode: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
// filter当中所关联到的属性必须在projection中
const filterNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
const checkFilterNode = (entity2: keyof ED, filterNode: ED[keyof ED]['Selection']['filter'], projectionNode: ED[keyof ED]['Selection']['data']) => {
const necessaryAttrs: string[] = ['id'];
for (const attr in filterNode) {
if (attr === '#id') {
assert(!filterNodeDict[filterNode[attr]!], `projection中结点的id有重复, ${filterNode[attr]}`);
Object.assign(filterNodeDict, {
[filterNode[attr]!]: projectionNode,
});
if (toBeAssignNode[filterNode[attr]!]) {
checkNode(projectionNode, toBeAssignNode[filterNode[attr]!]);
}
}
else if (['$and', '$or'].includes(attr)) {
for (const node of filterNode[attr]!) {
checkFilterNode(entity2, node, projectionNode);
}
}
else if (attr === '$not') {
checkFilterNode(entity2, filterNode[attr]!, projectionNode);
}
else if (attr === '$text') {
// 全文检索首先要有fulltext索引其次要把fulltext的相关属性加到projection里
const { indexes } = this.getSchema()[entity2];
const fulltextIndex = indexes!.find(
ele => ele.config && ele.config.type === 'fulltext'
);
const { attributes } = fulltextIndex!;
necessaryAttrs.push(...(attributes.map(ele => ele.name as string)));
}
else {
if (attr.toLowerCase().startsWith(EXPRESSION_PREFIX)) {
const exprResult = getAttrRefInExpression(filterNode[attr]!);
for (const nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (filterNodeDict[nodeName]) {
checkNode(filterNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode[nodeName]) {
toBeAssignNode[nodeName].push(...exprResult[nodeName]);
}
else {
Object.assign(toBeAssignNode, {
[nodeName]: exprResult[nodeName],
});
}
}
}
}
else {
const rel = this.judgeRelation(entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
if (!projectionNode[attr]) {
Object.assign(projectionNode, {
[attr]: {
id: 1,
}
});
}
checkFilterNode(attr, filterNode[attr]!, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push(`${attr}Id`);
if (!projectionNode[attr]) {
Object.assign(projectionNode, {
[attr]: {
id: 1,
}
});
}
checkFilterNode(rel, filterNode[attr]!, projectionNode[attr]);
}
else if (rel instanceof Array) {
// 现在filter中还不支持一对多的语义先放着吧
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
};
checkFilterNode(entity, filter, data);
relevantIds = getRelevantIds(filter);
}
// sorter感觉现在取不取影响不大前端的list直接获取返回的ids了先不管之
if (sorter) {
}
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
const projectionNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
const checkProjectionNode = (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
const necessaryAttrs: string[] = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据有时两个页面的filter的差异会导致有一个加createAt有一个不加此时可能产生前台取数据不完整的异常。先统一加上
for (const attr in projectionNode) {
if (attr === '#id') {
assert(!projectionNodeDict[projectionNode[attr]!], `projection中结点的id有重复, ${projectionNode[attr]}`);
Object.assign(projectionNodeDict, {
[projectionNode[attr]!]: projectionNode,
});
if (toBeAssignNode2[projectionNode[attr]!]) {
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]!]);
}
}
else {
if (attr.toLowerCase().startsWith(EXPRESSION_PREFIX)) {
const exprResult = getAttrRefInExpression(projectionNode[attr]!);
for (const nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (projectionNodeDict[nodeName]) {
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode2[nodeName]) {
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
}
else {
Object.assign(toBeAssignNode2, {
[nodeName]: exprResult[nodeName],
});
}
}
}
}
else {
const rel = judgeRelation(this.getSchema(), entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
checkProjectionNode(attr, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push(`${attr}Id`);
checkProjectionNode(rel, projectionNode[attr]);
}
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
const { data } = projectionNode[attr];
if (rel[1]) {
checkNode(data, [rel[1]]);
}
else {
checkNode(data, ['entity', 'entityId']);
}
this.reinforceSelection(rel[0], projectionNode[attr]);
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
// 如果对象中指向一对多的Modi此时加上指向Modi的projection
if (this.getSchema()[entity2].toModi) {
Object.assign(projectionNode, {
modi$entity: {
$entity: 'modi',
data: {
id: 1,
targetEntity: 1,
entity: 1,
entityId: 1,
action: 1,
iState: 1,
data: 1,
filter: 1,
},
filter: {
iState: 'active',
},
}
});
}
};
checkProjectionNode(entity, data);
if (!sorter && relevantIds.length === 0) {
// 如果没有sorter就给予一个按createAt逆序的sorter
Object.assign(selection, {
sorter: [
{
$attr: {
$$createAt$$: 1,
},
$direction: 'desc',
}
]
});
Object.assign(data, {
$$createAt$$: 1,
});
}
this.selectionRewriters.forEach(
ele => ele(this.getSchema(), entity, selection)
);
}
private reinforceOperation(entity: keyof ED, operation: ED[keyof ED]['Operation']) {
this.operationRewriters.forEach(
ele => ele(this.getSchema(), entity, operation)
);
}
public registerOperationRewriter(rewriter: OperationRewriter<ED>) {
this.operationRewriters.push(rewriter);
}
public registerSelectionRewriter(rewriter: SelectionRewriter<ED>) {
this.selectionRewriters.push(rewriter);
}
protected abstract selectAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(
entity: T,
selection: ED[T]['Selection'],
@ -131,6 +376,15 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
else {
cascadeSelectionFns.push(
(result) => {
const entityIds = uniq(result.filter(
ele => ele.entity === attr
).map(
ele => {
assert(ele.entityId !== null);
return ele.entityId;
}
) as string[]);
const dealWithSubRows = (subRows: Partial<ED[T]['Schema']>[]) => {
assert(subRows.length <= entityIds.length);
if (subRows.length < entityIds.length && !toModi) {
@ -167,14 +421,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
}
);
};
const entityIds = uniq(result.filter(
ele => ele.entity === attr
).map(
ele => {
assert(ele.entityId !== null);
return ele.entityId;
}
) as string[]);
if (entityIds.length > 0) {
const subRows = cascadeSelectFn.call(this, attr as any, {
@ -194,9 +440,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
dealWithSubRows(subRows as any);
}
}
else {
}
}
);
}
@ -248,6 +491,12 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
else {
cascadeSelectionFns.push(
(result) => {
const ids = uniq(result.filter(
ele => !!(ele[`${attr}Id`])
).map(
ele => ele[`${attr}Id`]
) as string[]);
const dealWithSubRows = (subRows: Partial<ED[keyof ED]['Schema']>[]) => {
assert(subRows.length <= ids.length);
if (subRows.length < ids.length && !toModi) {
@ -289,11 +538,6 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
}
);
};
const ids = uniq(result.filter(
ele => !!(ele[`${attr}Id`])
).map(
ele => ele[`${attr}Id`]
) as string[]);
if (ids.length > 0) {
const subRows = cascadeSelectFn.call(this, relation, {
@ -368,6 +612,14 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
) as string[];
const dealWithSubRows = (subRows: Partial<ED[keyof ED]['Schema']>[]) => {
// 这里如果result只有一行则把返回结果直接置上不对比外键值
// 这样做的原因是有的对象的filter会被改写掉userId),只能临时这样处理
if (result.length == 1) {
Object.assign(result[0], {
[attr]: subRows,
});
}
else {
result.forEach(
(ele) => {
const subRowss = subRows.filter(
@ -379,6 +631,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
});
}
);
}
};
if (ids.length > 0) {
@ -452,6 +705,14 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
ele => ele.id
) as string[];
const dealWithSubRows = (subRows: Partial<ED[T]['Schema']>[]) => {
// 这里如果result只有一行则把返回结果直接置上不对比外键值
// 这样做的原因是有的对象的filter会被改写掉userId),只能临时这样处理
if (result.length === 1) {
Object.assign(result[0], {
[attr]: subRows,
});
}
else {
result.forEach(
(ele) => {
const subRowss = subRows.filter(
@ -463,6 +724,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
});
}
);
}
};
if (ids.length > 0) {
@ -1038,9 +1300,9 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity as string)) {
// 按照框架要求生成Oper和OperEntity这两个内置的对象
assert(operId);
const operatorId = await context.getCurrentUserId(true);
const operatorId = context.getCurrentUserId(true);
if (operatorId) {
const createOper: CreateOperOperation = {
const createOper: CreateSingleOperOperation = {
id: 'dummy',
action: 'create',
data: {
@ -1048,6 +1310,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
action,
data,
operatorId,
targetEntity: entity as string,
operEntity$oper: data instanceof Array ? {
id: 'dummy',
action: 'create',
@ -1055,8 +1318,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
data.map(
async (ele) => ({
id: await generateNewIdAsync(),
entity: entity as string,
entityId: ele.id,
entity: entity as string,
})
)
),
@ -1065,8 +1328,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
action: 'create',
data: {
id: await generateNewIdAsync(),
entity: entity as string,
entityId: (data as ED[T]['CreateSingle']['data']).id,
entity: entity as string,
},
}]
},
@ -1190,13 +1453,14 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (!option?.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity as string) && ids.length > 0) {
// 按照框架要求生成Oper和OperEntity这两个内置的对象
assert(operId);
const createOper: CreateOperOperation = {
const createOper: CreateSingleOperOperation = {
id: 'dummy',
action: 'create',
data: {
id: operId,
action,
data,
targetEntity: entity as string,
operEntity$oper: {
id: 'dummy',
action: 'create',
@ -1204,8 +1468,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
ids.map(
async (ele) => ({
id: await generateNewIdAsync(),
entity: entity as string,
entityId: ele,
entity: entity as string,
})
)
)
@ -1344,7 +1608,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
operation: ED[T]['Operation'],
context: Cxt,
option: OP): OperationResult<ED> {
reinforceOperation(this.getSchema(), entity, operation);
this.reinforceOperation(entity, operation);
const { action, data, filter, id } = operation;
let opData: any;
const wholeBeforeFns: Array<() => any> = [];
@ -1409,7 +1673,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
operation: ED[T]['Operation'],
context: Cxt,
option: OP): Promise<OperationResult<ED>> {
reinforceOperation(this.getSchema(), entity, operation);
this.reinforceOperation(entity, operation);
const { action, data, filter, id } = operation;
let opData: any;
const wholeBeforeFns: Array<() => Promise<any>> = [];
@ -1472,7 +1736,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
selection: ED[T]['Selection'],
context: Cxt,
option: OP): Partial<ED[T]['Schema']>[] {
reinforceSelection(this.getSchema(), entity, selection);
this.reinforceSelection(entity, selection);
const { data, filter, indexFrom, count, sorter } = selection;
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(
entity,
@ -1571,11 +1835,11 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
const { id } = row;
if (!entityBranch![id!]) {
Object.assign(entityBranch!, {
[id!]: row,
[id!]: cloneDeep(row),
});
}
else {
Object.assign(entityBranch[id], row);
Object.assign(entityBranch[id], cloneDeep(row));
}
}
}
@ -1597,7 +1861,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (row) {
const { id } = row as { id: string };
Object.assign(entityBranch!, {
[id!]: row,
[id!]: cloneDeep(row),
});
}
}
@ -1612,7 +1876,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
selection: ED[T]['Selection'],
context: Cxt,
option: OP): Promise<Partial<ED[T]['Schema']>[]> {
reinforceSelection(this.getSchema(), entity, selection);
this.reinforceSelection(entity, selection);
const { data, filter, indexFrom, count, sorter } = selection;
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(
entity,

View File

@ -2,7 +2,7 @@ import assert from 'assert';
import { EntityDict, RowStore, OperateOption, OperationResult, SelectOption, TxnOption, Context, AggregationResult } from "../types";
export abstract class SyncContext<ED extends EntityDict> implements Context {
private rowStore: SyncRowStore<ED, this>;
rowStore: SyncRowStore<ED, this>;
private uuid?: string;
constructor(rowStore: SyncRowStore<ED, SyncContext<ED>>) {
this.rowStore = rowStore;

View File

@ -49,7 +49,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
registerChecker<T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): void {
const { entity, action, type, conditionalFilter } = checker;
const triggerName = `${String(entity)}${action}权限检查-${this.counter++}`;
const { fn, when } = translateCheckerInAsyncContext(checker, true);
const { fn, when } = translateCheckerInAsyncContext(checker);
const priority = type === 'data' ? DATA_CHECKER_DEFAULT_PRIORITY : CHECKER_DEFAULT_PRIORITY; // checker的默认优先级最低前面的trigger可能会赋上一些相应的值
const trigger = {
checkerType: type,

View File

@ -24,7 +24,7 @@ export function translateCheckerInAsyncContext<
ED extends EntityDict & BaseEntityDict,
T extends keyof ED,
Cxt extends AsyncContext<ED>
>(checker: Checker<ED, T, Cxt>, silent?: boolean): {
>(checker: Checker<ED, T, Cxt>): {
fn: Trigger<ED, T, Cxt>['fn'];
when: 'before' | 'after';
} {
@ -48,7 +48,7 @@ export function translateCheckerInAsyncContext<
const fn = (async ({ operation }, context, option) => {
const { filter: operationFilter, action } = operation;
const filter2 = typeof filter === 'function' ? await (filter as Function)(operation, context, option) : filter;
if (silent) {
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(operationFilter || {}, filter2);
return 0;
}
@ -106,7 +106,7 @@ export function translateCheckerInAsyncContext<
console.warn(`${entity as string}对象的create类型的checker中存在无法转换为表达式形式的情况请尽量使用authDef格式定义这类checker`);
return 0;
}
if (silent) {
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(filter || {}, result);
return 0;
}
@ -405,7 +405,7 @@ function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityD
}
if (filter.$or) {
const countersOr = filter.$and.map(
const countersOr = filter.$or.map(
(ele: ED[keyof ED]['Selection']['filter']) => translateCreateFilterMaker(entity, ele, userId)
) as ReturnType<typeof translateCreateFilterMaker>[];
// or也只要有一个满足就行不能否定

View File

@ -1,275 +0,0 @@
import assert from 'assert';
import { getAttrRefInExpression, StorageSchema } from '../types';
import { EXPRESSION_PREFIX } from '../types/Demand';
import { EntityDict } from '../types/Entity';
import { getRelevantIds } from './filter';
import { judgeRelation } from './relation';
type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
const SelectionRewriters: SelectionRewriter<any>[] = [];
export function registerSelectionRewriter<ED extends EntityDict>(rewriter: SelectionRewriter<ED>) {
SelectionRewriters.push(rewriter);
}
function getSelectionRewriters<ED extends EntityDict>() {
return SelectionRewriters as SelectionRewriter<ED>[];
}
type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
const OperationRewriters: OperationRewriter<any>[] = [];
export function registerOperationRewriter<ED extends EntityDict>(rewriter: OperationRewriter<ED>) {
OperationRewriters.push(rewriter);
}
function getOperationRewriters<ED extends EntityDict>() {
return OperationRewriters as OperationRewriter<ED>[];
}
/**
* selection进行一些完善
* @param selection
*/
export function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) {
const { filter, data, sorter } = selection;
const checkNode = (projectionNode: ED[keyof ED]['Selection']['data'], attrs: string[]) => {
attrs.forEach(
(attr) => {
if (!projectionNode.hasOwnProperty(attr)) {
Object.assign(projectionNode, {
[attr]: 1,
});
}
}
);
};
let relevantIds: string[] = [];
if (filter) {
const toBeAssignNode: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
// filter当中所关联到的属性必须在projection中
const filterNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
const checkFilterNode = (entity2: keyof ED, filterNode: ED[keyof ED]['Selection']['filter'], projectionNode: ED[keyof ED]['Selection']['data']) => {
const necessaryAttrs: string[] = ['id'];
for (const attr in filterNode) {
if (attr === '#id') {
assert(!filterNodeDict[filterNode[attr]!], `projection中结点的id有重复, ${filterNode[attr]}`);
Object.assign(filterNodeDict, {
[filterNode[attr]!]: projectionNode,
});
if (toBeAssignNode[filterNode[attr]!]) {
checkNode(projectionNode, toBeAssignNode[filterNode[attr]!]);
}
}
else if (['$and', '$or'].includes(attr)) {
for (const node of filterNode[attr]!) {
checkFilterNode(entity2, node, projectionNode);
}
}
else if (attr === '$not') {
checkFilterNode(entity2, filterNode[attr]!, projectionNode);
}
else if (attr === '$text') {
// 全文检索首先要有fulltext索引其次要把fulltext的相关属性加到projection里
const { indexes } = schema[entity2];
const fulltextIndex = indexes!.find(
ele => ele.config && ele.config.type === 'fulltext'
);
const { attributes } = fulltextIndex!;
necessaryAttrs.push(...(attributes.map(ele => ele.name as string)));
}
else {
if (attr.toLowerCase().startsWith(EXPRESSION_PREFIX)) {
const exprResult = getAttrRefInExpression(filterNode[attr]!);
for (const nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (filterNodeDict[nodeName]) {
checkNode(filterNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode[nodeName]) {
toBeAssignNode[nodeName].push(...exprResult[nodeName]);
}
else {
Object.assign(toBeAssignNode, {
[nodeName]: exprResult[nodeName],
});
}
}
}
}
else {
const rel = judgeRelation(schema, entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
if (!projectionNode[attr]) {
Object.assign(projectionNode, {
[attr]: {
id: 1,
}
});
}
checkFilterNode(attr, filterNode[attr]!, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push(`${attr}Id`);
if (!projectionNode[attr]) {
Object.assign(projectionNode, {
[attr]: {
id: 1,
}
});
}
checkFilterNode(rel, filterNode[attr]!, projectionNode[attr]);
}
else if (rel instanceof Array) {
// 现在filter中还不支持一对多的语义先放着吧
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
};
checkFilterNode(entity, filter, data);
relevantIds = getRelevantIds(filter);
}
// sorter感觉现在取不取影响不大前端的list直接获取返回的ids了先不管之
if (sorter) {
}
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
const projectionNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
const checkProjectionNode = (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
const necessaryAttrs: string[] = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据有时两个页面的filter的差异会导致有一个加createAt有一个不加此时可能产生前台取数据不完整的异常。先统一加上
for (const attr in projectionNode) {
if (attr === '#id') {
assert(!projectionNodeDict[projectionNode[attr]!], `projection中结点的id有重复, ${projectionNode[attr]}`);
Object.assign(projectionNodeDict, {
[projectionNode[attr]!]: projectionNode,
});
if (toBeAssignNode2[projectionNode[attr]!]) {
checkNode(projectionNode, toBeAssignNode2[projectionNode[attr]!]);
}
}
else {
if (attr.toLowerCase().startsWith(EXPRESSION_PREFIX)) {
const exprResult = getAttrRefInExpression(projectionNode[attr]!);
for (const nodeName in exprResult) {
if (nodeName === '#current') {
checkNode(projectionNode, exprResult[nodeName]);
}
else if (projectionNodeDict[nodeName]) {
checkNode(projectionNodeDict[nodeName], exprResult[nodeName]);
}
else {
if (toBeAssignNode2[nodeName]) {
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
}
else {
Object.assign(toBeAssignNode2, {
[nodeName]: exprResult[nodeName],
});
}
}
}
}
else {
const rel = judgeRelation(schema, entity2, attr);
if (rel === 1) {
necessaryAttrs.push(attr);
}
else if (rel === 2) {
// entity/entityId反指
necessaryAttrs.push('entity', 'entityId');
checkProjectionNode(attr, projectionNode[attr]);
}
else if (typeof rel === 'string') {
necessaryAttrs.push(`${attr}Id`);
checkProjectionNode(rel, projectionNode[attr]);
}
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
const { data } = projectionNode[attr];
if (rel[1]) {
checkNode(data, [rel[1]]);
}
else {
checkNode(data, ['entity', 'entityId']);
}
reinforceSelection(schema, rel[0], projectionNode[attr]);
}
}
}
checkNode(projectionNode, necessaryAttrs);
}
// 如果对象中指向一对多的Modi此时加上指向Modi的projection
if (schema[entity2].toModi) {
Object.assign(projectionNode, {
modi$entity: {
$entity: 'modi',
data: {
id: 1,
targetEntity: 1,
entity: 1,
entityId: 1,
action: 1,
iState: 1,
data: 1,
filter: 1,
},
filter: {
iState: 'active',
},
}
});
}
};
checkProjectionNode(entity, data);
if (!sorter && relevantIds.length === 0) {
// 如果没有sorter就给予一个按createAt逆序的sorter
Object.assign(selection, {
sorter: [
{
$attr: {
$$createAt$$: 1,
},
$direction: 'desc',
}
]
});
Object.assign(data, {
$$createAt$$: 1,
});
}
SelectionRewriters.forEach(
ele => ele(schema, entity, selection)
);
}
/**
* operation进行一些完善operation算子的注入点
* @param schema
* @param entity
* @param selection
*/
export function reinforceOperation<ED extends EntityDict>(schema: StorageSchema<ED>, entity: keyof ED, operation: ED[keyof ED]['Operation']) {
OperationRewriters.forEach(
ele => ele(schema, entity, operation)
);
}

67
src/timers/oper.ts Normal file
View File

@ -0,0 +1,67 @@
import { EntityDict } from '../types/Entity';
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
import { vaccumEntities } from './vaccum';
import { combineFilters } from '../store/filter';
export type VaccumOperOption<ED extends EntityDict & BaseEntityDict> = {
aliveLine: number;
excludeOpers?: {
[T in keyof ED]?: ED[T]['Action'][];
};
backupDir?: string;
zip?: boolean;
};
/**
* oper对象清空
* @param option
* @param context
* @returns
*/
export async function vaccumOper<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOperOption<ED>, context: Cxt) {
const { aliveLine, excludeOpers, ...rest } = option;
const operFilter: ED['oper']['Selection']['filter'] = {};
if (excludeOpers) {
const notFilters: ED['oper']['Selection']['filter'][] = [];
for (const key in excludeOpers) {
if (excludeOpers[key]!.length > 0) {
notFilters.push({
targetEntity: key,
action: {
$in: excludeOpers[key],
}
} as ED['oper']['Selection']['filter']);
}
else {
notFilters.push({
targetEntity: key,
});
}
}
if (notFilters.length > 0) {
operFilter.$not = {
$or: notFilters as NonNullable<ED['oper']['Selection']['filter']>[],
};
}
}
return vaccumEntities({
entities: [{
entity: 'operEntity',
aliveLine: aliveLine + 10000,
filter: {
oper: combineFilters([operFilter, {
$$createAt$$: {
$lt: aliveLine,
}
}]),
},
}, {
entity: 'oper',
aliveLine,
filter: operFilter,
}],
...rest,
}, context);
}

134
src/timers/vaccum.ts Normal file
View File

@ -0,0 +1,134 @@
import dayJs from 'dayjs';
import { appendFileSync, existsSync, openSync, rmSync, closeSync, createReadStream, createWriteStream } from 'fs';
import { EntityDict } from '../types/Entity';
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
import { combineFilters } from '../store/filter';
import { createGzip } from 'node:zlib';
import { pipeline } from 'stream';
import { generateNewIdAsync } from '../utils/uuid';
type VaccumOptionEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
entity: T;
filter?: ED[T]['Selection']['filter']; // 如果有额外的条件放在filter中满足条件的才会被清空
aliveLine: number; // vaccum一定是按createAt清空数据在aliveLine之后的数据不会被清空
};
type VaccumOption<ED extends EntityDict & BaseEntityDict> = {
entities: Array<VaccumOptionEntity<ED, keyof ED>>;
backupDir?: string;
zip?: boolean;
};
/**
*
*
* @param option
*/
export async function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOption<ED>, context: Cxt) {
const { entities, backupDir } = option;
for (const ele of entities) {
const { entity, filter, aliveLine } = ele;
let filter2: ED[keyof ED]['Selection']['filter'] = {
$$createAt$$: {
$lt: aliveLine,
},
};
if (filter) {
filter2 = combineFilters([filter2, filter]);
}
if (backupDir) {
// 使用mysqldump将待删除的数据备份出来
const { zip: zip } = option;
const now = dayJs();
const backFile = `${backupDir}/${entity as string}-${now.format('YYYY-MM-DD HH:mm:ss')}.csv`;
if (existsSync(backFile)) {
rmSync(backFile);
}
const fd = openSync(backFile, 'a');
const attributes = ['id', '$$createAt$$', '$$updateAt$$', '$$deleteAt$$'];
const projection: ED[keyof ED]['Selection']['data']= {
id: 1,
$$createAt$$: 1,
$$updateAt$$: 1,
$$deleteAt$$: 1,
};
for (const attr in context.getSchema()[entity]!.attributes) {
Object.assign(projection, {
[attr]: 1,
});
attributes.push(attr);
}
appendFileSync(fd, attributes.join(','));
appendFileSync(fd, '\n');
let count = 0;
const appendData = async (minCreateAt: number): Promise<void> => {
const filter3 = combineFilters([filter2, {
$$createAt$$: {
$gt: minCreateAt,
},
}]);
const rows = await context.select(entity, {
data: projection,
filter: filter3,
sorter: [{
$attr: {
$$createAt$$: 1,
},
$direction: 'asc'
}],
indexFrom: 0,
count: 1000,
}, { includedDeleted: true });
const csvTxt = rows.map(
(row) => attributes.map(
(attr) => JSON.stringify(row[attr])
).join(',')
).join('\n');
appendFileSync(fd, csvTxt);
appendFileSync(fd, '\n');
count += rows.length;
if (rows.length === 1000) {
const maxCreateAt = rows[999].$$createAt$$;
return appendData(maxCreateAt as number);
}
};
await appendData(0);
closeSync(fd);
console.log(`备份${entity as string}对象完毕,共备份了${count}行数据`);
if (count === 0) {
rmSync(backFile);
}
else if (zip) {
const gzip = createGzip();
const source = createReadStream(backFile);
const destination = createWriteStream(`${backFile}.zip`);
await new Promise(
(resolve, reject) => {
pipeline(source, gzip, destination, (err) => {
if (err) {
reject(err);
}
else {
resolve(undefined);
}
})
}
);
}
}
// 将对应的数据删除
await context.operate(entity, {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: filter2,
}, { deletePhysically: true });
}
}

View File

@ -16,10 +16,10 @@ export abstract class Connector<ED extends EntityDict, BackCxt extends AsyncCont
abstract parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{ name: string; params: any; context: BackCxt; }>;
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): {
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): Promise<{
body: any;
headers?: Record<string, any>;
};
}>;
abstract serializeException(exception: OakException<ED>, headers: IncomingHttpHeaders, body: any): {
body: any;

View File

@ -43,6 +43,7 @@ export type OperateOption = {
allowExists?: boolean; // 插入时允许已经存在唯一键值的行了即insert / update逻辑
modiParentId?: string; // 如果是延时更新相关modi要关联到一个父亲上统一应用
modiParentEntity?: string; // 如果是延时更新相关modi要关联到一个父亲上统一应用
deletePhysically?: boolean;
dummy?: 1; // 无用为了继承Option通过编译
};

View File

@ -6,6 +6,10 @@ export type TxnOption = {
isolationLevel: 'repeatable read' | 'serializable';
};
export type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
export type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
export abstract class RowStore<ED extends EntityDict> {
protected storageSchema: StorageSchema<ED>;
@ -13,6 +17,10 @@ export abstract class RowStore<ED extends EntityDict> {
this.storageSchema = storageSchema;
}
abstract registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
getSchema () {
return this.storageSchema;
}

View File

@ -104,12 +104,14 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
};
}
serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): { body: any; headers?: Record<string, any> | undefined; } {
async serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): Promise<{ body: any; headers?: Record<string, any> | undefined; }> {
if (result instanceof Stream || result instanceof Buffer) {
return {
body: result,
};
}
await context.refineOpRecords();
return {
body: {
result,