From 18996c86a70e9af335b0f8ab050d8e2ae7b0fbdb Mon Sep 17 00:00:00 2001 From: Xc Date: Fri, 14 Apr 2023 18:01:51 +0800 Subject: [PATCH 01/14] 2.6.9-dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca4cd34..8d12c3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oak-domain", - "version": "2.6.8", + "version": "2.6.9", "author": { "name": "XuChang" }, From 7d01ada8958d262be089f7e8733e10cdfe0aeb20 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Mon, 17 Apr 2023 15:07:18 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86relation?= =?UTF-8?q?=E4=B8=ADerrMsg=E7=9A=84=E5=87=BA=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base-app-domain/ActionAuth/Schema.d.ts | 156 ++++++++++++++++ lib/base-app-domain/ActionAuth/Schema.js | 2 + lib/base-app-domain/ActionAuth/Storage.d.ts | 3 + lib/base-app-domain/ActionAuth/Storage.js | 49 +++++ lib/base-app-domain/EntityDict.d.ts | 8 + lib/base-app-domain/ModiEntity/Schema.d.ts | 116 +++++++++++- lib/base-app-domain/ModiEntity/Storage.js | 2 +- lib/base-app-domain/OperEntity/Schema.d.ts | 116 +++++++++++- lib/base-app-domain/OperEntity/Storage.js | 2 +- lib/base-app-domain/Relation/Schema.d.ts | 160 ++++++++++++++++ lib/base-app-domain/Relation/Schema.js | 2 + lib/base-app-domain/Relation/Storage.d.ts | 3 + lib/base-app-domain/Relation/Storage.js | 57 ++++++ lib/base-app-domain/RelationAuth/Schema.d.ts | 156 ++++++++++++++++ lib/base-app-domain/RelationAuth/Schema.js | 2 + lib/base-app-domain/RelationAuth/Storage.d.ts | 3 + lib/base-app-domain/RelationAuth/Storage.js | 49 +++++ lib/base-app-domain/Storage.js | 32 ++-- lib/base-app-domain/User/Schema.d.ts | 11 ++ lib/base-app-domain/UserRelation/Schema.d.ts | 175 ++++++++++++++++++ lib/base-app-domain/UserRelation/Schema.js | 2 + lib/base-app-domain/UserRelation/Storage.d.ts | 3 + lib/base-app-domain/UserRelation/Storage.js | 36 ++++ lib/base-app-domain/_SubQuery.d.ts | 32 ++++ lib/entities/ActionAuth.d.ts | 11 ++ lib/entities/ActionAuth.js | 30 +++ lib/entities/Relation.d.ts | 8 + lib/entities/Relation.js | 33 ++++ lib/entities/RelationAuth.d.ts | 11 ++ lib/entities/RelationAuth.js | 30 +++ lib/entities/UserRelation.d.ts | 7 + lib/entities/UserRelation.js | 28 +++ lib/store/checker.js | 24 ++- lib/types/Auth.d.ts | 2 +- src/entities/ActionAuth.ts | 49 +++++ src/entities/Relation.ts | 49 +++++ src/entities/RelationAuth.ts | 49 +++++ src/entities/UserRelation.ts | 44 +++++ src/store/checker.ts | 23 ++- src/types/Auth.ts | 2 +- tsconfig.json | 2 +- 41 files changed, 1543 insertions(+), 36 deletions(-) create mode 100644 lib/base-app-domain/ActionAuth/Schema.d.ts create mode 100644 lib/base-app-domain/ActionAuth/Schema.js create mode 100644 lib/base-app-domain/ActionAuth/Storage.d.ts create mode 100644 lib/base-app-domain/ActionAuth/Storage.js create mode 100644 lib/base-app-domain/Relation/Schema.d.ts create mode 100644 lib/base-app-domain/Relation/Schema.js create mode 100644 lib/base-app-domain/Relation/Storage.d.ts create mode 100644 lib/base-app-domain/Relation/Storage.js create mode 100644 lib/base-app-domain/RelationAuth/Schema.d.ts create mode 100644 lib/base-app-domain/RelationAuth/Schema.js create mode 100644 lib/base-app-domain/RelationAuth/Storage.d.ts create mode 100644 lib/base-app-domain/RelationAuth/Storage.js create mode 100644 lib/base-app-domain/UserRelation/Schema.d.ts create mode 100644 lib/base-app-domain/UserRelation/Schema.js create mode 100644 lib/base-app-domain/UserRelation/Storage.d.ts create mode 100644 lib/base-app-domain/UserRelation/Storage.js create mode 100644 lib/entities/ActionAuth.d.ts create mode 100644 lib/entities/ActionAuth.js create mode 100644 lib/entities/Relation.d.ts create mode 100644 lib/entities/Relation.js create mode 100644 lib/entities/RelationAuth.d.ts create mode 100644 lib/entities/RelationAuth.js create mode 100644 lib/entities/UserRelation.d.ts create mode 100644 lib/entities/UserRelation.js create mode 100644 src/entities/ActionAuth.ts create mode 100644 src/entities/Relation.ts create mode 100644 src/entities/RelationAuth.ts create mode 100644 src/entities/UserRelation.ts diff --git a/lib/base-app-domain/ActionAuth/Schema.d.ts b/lib/base-app-domain/ActionAuth/Schema.d.ts new file mode 100644 index 0000000..7b7aad0 --- /dev/null +++ b/lib/base-app-domain/ActionAuth/Schema.d.ts @@ -0,0 +1,156 @@ +import { String, ForeignKey } from "../../types/DataType"; +import { Q_DateValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "../../types/Demand"; +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 Relation from "../Relation/Schema"; +import * as ModiEntity from "../ModiEntity/Schema"; +import * as OperEntity from "../OperEntity/Schema"; +declare type Actions = string[]; +export declare type OpSchema = EntityShape & { + relationId: ForeignKey<"relation">; + path: String<256>; + destEntity: String<32>; + deActions: Actions; +}; +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { + relationId: ForeignKey<"relation">; + path: String<256>; + destEntity: String<32>; + deActions: Actions; + relation: Relation.Schema; + modiEntity$entity?: Array; + modiEntity$entity$$aggr?: AggregationResult; + operEntity$entity?: Array; + operEntity$entity$$aggr?: AggregationResult; +} & { + [A in ExpressionKey]?: any; +}; +declare type AttrFilter = { + id: Q_StringValue | SubQuery.ActionAuthIdSubQuery; + $$createAt$$: Q_DateValue; + $$seq$$: Q_StringValue; + $$updateAt$$: Q_DateValue; + relationId: Q_StringValue | SubQuery.RelationIdSubQuery; + relation: Relation.Filter; + path: Q_StringValue; + destEntity: Q_StringValue; + deActions: Q_EnumValue; +}; +export declare type Filter = MakeFilter>; +export declare type Projection = { + "#id"?: NodeId; + [k: string]: any; + id?: number; + $$createAt$$?: number; + $$updateAt$$?: number; + $$seq$$?: number; + relationId?: number; + relation?: Relation.Projection; + path?: number; + destEntity?: number; + deActions?: number; + 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>; +declare type ActionAuthIdProjection = OneOf<{ + id: number; +}>; +declare type RelationIdProjection = OneOf<{ + relationId: number; +}>; +export declare type SortAttr = { + id: number; +} | { + $$createAt$$: number; +} | { + $$seq$$: number; +} | { + $$updateAt$$: number; +} | { + relationId: number; +} | { + relation: Relation.SortAttr; +} | { + path: number; +} | { + destEntity: number; +} | { + deActions: number; +} | { + [k: string]: any; +} | OneOf>; +export declare type SortNode = { + $attr: SortAttr; + $direction?: "asc" | "desc"; +}; +export declare type Sorter = SortNode[]; +export declare type SelectOperation

= OakSelection<"select", P, Filter, Sorter>; +export declare type Selection

= Omit, "action">; +export declare type Aggregation = DeduceAggregation; +export declare type CreateOperationData = FormCreateData> & (({ + relationId?: never; + relation: Relation.CreateSingleOperation; +} | { + relationId: String<64>; + relation?: Relation.UpdateOperation; +} | { + relationId: String<64>; +})) & { + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>; +export declare type CreateMultipleOperation = OakOperation<"create", Array>; +export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation; +export declare type UpdateOperationData = FormUpdateData> & (({ + relation: Relation.CreateSingleOperation; + relationId?: never; +} | { + relation: Relation.UpdateOperation; + relationId?: never; +} | { + relation: Relation.RemoveOperation; + relationId?: never; +} | { + relation?: never; + relationId?: String<64> | null; +})) & { + [k: string]: any; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & (({ + relation?: Relation.UpdateOperation | Relation.RemoveOperation; +})); +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type RelationIdSubQuery = Selection; +export declare type ActionAuthIdSubQuery = Selection; +export declare type EntityDef = { + Schema: Schema; + OpSchema: OpSchema; + Action: OakMakeAction | string; + Selection: Selection; + Aggregation: Aggregation; + Operation: Operation; + Create: CreateOperation; + Update: UpdateOperation; + Remove: RemoveOperation; + CreateSingle: CreateSingleOperation; + CreateMulti: CreateMultipleOperation; +}; +export {}; diff --git a/lib/base-app-domain/ActionAuth/Schema.js b/lib/base-app-domain/ActionAuth/Schema.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/base-app-domain/ActionAuth/Schema.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/base-app-domain/ActionAuth/Storage.d.ts b/lib/base-app-domain/ActionAuth/Storage.d.ts new file mode 100644 index 0000000..606c158 --- /dev/null +++ b/lib/base-app-domain/ActionAuth/Storage.d.ts @@ -0,0 +1,3 @@ +import { StorageDesc } from "../../types/Storage"; +import { OpSchema } from "./Schema"; +export declare const desc: StorageDesc; diff --git a/lib/base-app-domain/ActionAuth/Storage.js b/lib/base-app-domain/ActionAuth/Storage.js new file mode 100644 index 0000000..2996621 --- /dev/null +++ b/lib/base-app-domain/ActionAuth/Storage.js @@ -0,0 +1,49 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.desc = void 0; +var action_1 = require("../../actions/action"); +exports.desc = { + attributes: { + relationId: { + notNull: true, + type: "ref", + ref: "relation" + }, + path: { + notNull: true, + type: "varchar", + params: { + length: 256 + } + }, + destEntity: { + notNull: true, + type: "varchar", + params: { + length: 32 + } + }, + deActions: { + notNull: true, + type: "object" + } + }, + actionType: "crud", + actions: action_1.genericActions, + indexes: [ + { + name: 'index_relation_path', + attributes: [ + { + name: "relationId" + }, + { + name: 'path' + }, + ], + config: { + unique: true + } + } + ] +}; diff --git a/lib/base-app-domain/EntityDict.d.ts b/lib/base-app-domain/EntityDict.d.ts index 3d22998..73937b9 100644 --- a/lib/base-app-domain/EntityDict.d.ts +++ b/lib/base-app-domain/EntityDict.d.ts @@ -1,14 +1,22 @@ +import { EntityDef as ActionAuth } from "./ActionAuth/Schema"; import { EntityDef as Modi } from "./Modi/Schema"; import { EntityDef as ModiEntity } from "./ModiEntity/Schema"; import { EntityDef as Oper } from "./Oper/Schema"; import { EntityDef as OperEntity } from "./OperEntity/Schema"; +import { EntityDef as Relation } from "./Relation/Schema"; +import { EntityDef as RelationAuth } from "./RelationAuth/Schema"; import { EntityDef as User } from "./User/Schema"; import { EntityDef as UserEntityGrant } from "./UserEntityGrant/Schema"; +import { EntityDef as UserRelation } from "./UserRelation/Schema"; export declare type EntityDict = { + actionAuth: ActionAuth; modi: Modi; modiEntity: ModiEntity; oper: Oper; operEntity: OperEntity; + relation: Relation; + relationAuth: RelationAuth; user: User; userEntityGrant: UserEntityGrant; + userRelation: UserRelation; }; diff --git a/lib/base-app-domain/ModiEntity/Schema.d.ts b/lib/base-app-domain/ModiEntity/Schema.d.ts index d7ec3fc..3b27a68 100644 --- a/lib/base-app-domain/ModiEntity/Schema.d.ts +++ b/lib/base-app-domain/ModiEntity/Schema.d.ts @@ -5,21 +5,29 @@ 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 Modi from "../Modi/Schema"; +import * as ActionAuth from "../ActionAuth/Schema"; +import * as Relation from "../Relation/Schema"; +import * as RelationAuth from "../RelationAuth/Schema"; import * as User from "../User/Schema"; import * as UserEntityGrant from "../UserEntityGrant/Schema"; +import * as UserRelation from "../UserRelation/Schema"; export declare type OpSchema = EntityShape & { modiId: ForeignKey<"modi">; - entity: "user" | "userEntityGrant" | string; + entity: "actionAuth" | "relation" | "relationAuth" | "user" | "userEntityGrant" | "userRelation" | string; entityId: String<64>; }; export declare type OpAttr = keyof OpSchema; export declare type Schema = EntityShape & { modiId: ForeignKey<"modi">; - entity: "user" | "userEntityGrant" | string; + entity: "actionAuth" | "relation" | "relationAuth" | "user" | "userEntityGrant" | "userRelation" | string; entityId: String<64>; modi: Modi.Schema; + actionAuth?: ActionAuth.Schema; + relation?: Relation.Schema; + relationAuth?: RelationAuth.Schema; user?: User.Schema; userEntityGrant?: UserEntityGrant.Schema; + userRelation?: UserRelation.Schema; } & { [A in ExpressionKey]?: any; }; @@ -32,10 +40,14 @@ declare type AttrFilter = { modi: Modi.Filter; entity: E; entityId: Q_StringValue; + actionAuth: ActionAuth.Filter; + relation: Relation.Filter; + relationAuth: RelationAuth.Filter; user: User.Filter; userEntityGrant: UserEntityGrant.Filter; + userRelation: UserRelation.Filter; }; -export declare type Filter> = MakeFilter & ExprOp>; +export declare type Filter> = MakeFilter & ExprOp>; export declare type Projection = { "#id"?: NodeId; [k: string]: any; @@ -47,8 +59,12 @@ export declare type Projection = { modi?: Modi.Projection; entity?: number; entityId?: number; + actionAuth?: ActionAuth.Projection; + relation?: Relation.Projection; + relationAuth?: RelationAuth.Projection; user?: User.Projection; userEntityGrant?: UserEntityGrant.Projection; + userRelation?: UserRelation.Projection; } & Partial>; declare type ModiEntityIdProjection = OneOf<{ id: number; @@ -56,12 +72,24 @@ declare type ModiEntityIdProjection = OneOf<{ declare type ModiIdProjection = OneOf<{ modiId: number; }>; +declare type ActionAuthIdProjection = OneOf<{ + entityId: number; +}>; +declare type RelationIdProjection = OneOf<{ + entityId: number; +}>; +declare type RelationAuthIdProjection = OneOf<{ + entityId: number; +}>; declare type UserIdProjection = OneOf<{ entityId: number; }>; declare type UserEntityGrantIdProjection = OneOf<{ entityId: number; }>; +declare type UserRelationIdProjection = OneOf<{ + entityId: number; +}>; export declare type SortAttr = { id: number; } | { @@ -78,10 +106,18 @@ export declare type SortAttr = { entity: number; } | { entityId: number; +} | { + actionAuth: ActionAuth.SortAttr; +} | { + relation: Relation.SortAttr; +} | { + relationAuth: RelationAuth.SortAttr; } | { user: User.SortAttr; } | { userEntityGrant: UserEntityGrant.SortAttr; +} | { + userRelation: UserRelation.SortAttr; } | { [k: string]: any; } | OneOf>; @@ -102,6 +138,39 @@ export declare type CreateOperationData = FormCreateData; })) & ({ + entity?: never; + entityId?: never; + actionAuth: ActionAuth.CreateSingleOperation; +} | { + entity: "actionAuth"; + entityId: String<64>; + actionAuth: ActionAuth.UpdateOperation; +} | { + entity: "actionAuth"; + entityId: String<64>; +} | { + entity?: never; + entityId?: never; + relation: Relation.CreateSingleOperation; +} | { + entity: "relation"; + entityId: String<64>; + relation: Relation.UpdateOperation; +} | { + entity: "relation"; + entityId: String<64>; +} | { + entity?: never; + entityId?: never; + relationAuth: RelationAuth.CreateSingleOperation; +} | { + entity: "relationAuth"; + entityId: String<64>; + relationAuth: RelationAuth.UpdateOperation; +} | { + entity: "relationAuth"; + entityId: String<64>; +} | { entity?: never; entityId?: never; user: User.CreateSingleOperation; @@ -123,6 +192,17 @@ export declare type CreateOperationData = FormCreateData; +} | { + entity?: never; + entityId?: never; + userRelation: UserRelation.CreateSingleOperation; +} | { + entity: "userRelation"; + entityId: String<64>; + userRelation: UserRelation.UpdateOperation; +} | { + entity: "userRelation"; + entityId: String<64>; } | { entity?: string; entityId?: string; @@ -144,6 +224,18 @@ export declare type UpdateOperationData = FormUpdateData | null; })) & ({ + actionAuth?: ActionAuth.CreateSingleOperation | ActionAuth.UpdateOperation | ActionAuth.RemoveOperation; + entityId?: never; + entity?: never; +} | { + relation?: Relation.CreateSingleOperation | Relation.UpdateOperation | Relation.RemoveOperation; + entityId?: never; + entity?: never; +} | { + relationAuth?: RelationAuth.CreateSingleOperation | RelationAuth.UpdateOperation | RelationAuth.RemoveOperation; + entityId?: never; + entity?: never; +} | { user?: User.CreateSingleOperation | User.UpdateOperation | User.RemoveOperation; entityId?: never; entity?: never; @@ -152,7 +244,11 @@ export declare type UpdateOperationData = FormUpdateData | null; }) & { [k: string]: any; @@ -161,17 +257,29 @@ export declare type UpdateOperation = OakOperation<"update" | string, UpdateOper export declare type RemoveOperationData = {} & (({ modi?: Modi.UpdateOperation | Modi.RemoveOperation; })) & ({ + actionAuth?: ActionAuth.UpdateOperation | ActionAuth.RemoveOperation; +} | { + relation?: Relation.UpdateOperation | Relation.RemoveOperation; +} | { + relationAuth?: RelationAuth.UpdateOperation | RelationAuth.RemoveOperation; +} | { user?: User.UpdateOperation | User.RemoveOperation; } | { userEntityGrant?: UserEntityGrant.UpdateOperation | UserEntityGrant.RemoveOperation; +} | { + userRelation?: UserRelation.UpdateOperation | UserRelation.RemoveOperation; } | { [k: string]: any; }); export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; export declare type ModiIdSubQuery = Selection; +export declare type ActionAuthIdSubQuery = Selection; +export declare type RelationIdSubQuery = Selection; +export declare type RelationAuthIdSubQuery = Selection; export declare type UserIdSubQuery = Selection; export declare type UserEntityGrantIdSubQuery = Selection; +export declare type UserRelationIdSubQuery = Selection; export declare type ModiEntityIdSubQuery = Selection; export declare type EntityDef = { Schema: Schema; diff --git a/lib/base-app-domain/ModiEntity/Storage.js b/lib/base-app-domain/ModiEntity/Storage.js index eee9ae2..6a3a8c2 100644 --- a/lib/base-app-domain/ModiEntity/Storage.js +++ b/lib/base-app-domain/ModiEntity/Storage.js @@ -15,7 +15,7 @@ exports.desc = { params: { length: 32 }, - ref: ["user", "userEntityGrant"] + ref: ["actionAuth", "relation", "relationAuth", "user", "userEntityGrant", "userRelation"] }, entityId: { notNull: true, diff --git a/lib/base-app-domain/OperEntity/Schema.d.ts b/lib/base-app-domain/OperEntity/Schema.d.ts index 45e8505..e3ca574 100644 --- a/lib/base-app-domain/OperEntity/Schema.d.ts +++ b/lib/base-app-domain/OperEntity/Schema.d.ts @@ -5,21 +5,29 @@ 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 ActionAuth from "../ActionAuth/Schema"; +import * as Relation from "../Relation/Schema"; +import * as RelationAuth from "../RelationAuth/Schema"; import * as User from "../User/Schema"; import * as UserEntityGrant from "../UserEntityGrant/Schema"; +import * as UserRelation from "../UserRelation/Schema"; export declare type OpSchema = EntityShape & { operId: ForeignKey<"oper">; - entity: "user" | "userEntityGrant" | string; + entity: "actionAuth" | "relation" | "relationAuth" | "user" | "userEntityGrant" | "userRelation" | string; entityId: String<64>; }; export declare type OpAttr = keyof OpSchema; export declare type Schema = EntityShape & { operId: ForeignKey<"oper">; - entity: "user" | "userEntityGrant" | string; + entity: "actionAuth" | "relation" | "relationAuth" | "user" | "userEntityGrant" | "userRelation" | string; entityId: String<64>; oper: Oper.Schema; + actionAuth?: ActionAuth.Schema; + relation?: Relation.Schema; + relationAuth?: RelationAuth.Schema; user?: User.Schema; userEntityGrant?: UserEntityGrant.Schema; + userRelation?: UserRelation.Schema; } & { [A in ExpressionKey]?: any; }; @@ -32,10 +40,14 @@ declare type AttrFilter = { oper: Oper.Filter; entity: E; entityId: Q_StringValue; + actionAuth: ActionAuth.Filter; + relation: Relation.Filter; + relationAuth: RelationAuth.Filter; user: User.Filter; userEntityGrant: UserEntityGrant.Filter; + userRelation: UserRelation.Filter; }; -export declare type Filter> = MakeFilter & ExprOp>; +export declare type Filter> = MakeFilter & ExprOp>; export declare type Projection = { "#id"?: NodeId; [k: string]: any; @@ -47,8 +59,12 @@ export declare type Projection = { oper?: Oper.Projection; entity?: number; entityId?: number; + actionAuth?: ActionAuth.Projection; + relation?: Relation.Projection; + relationAuth?: RelationAuth.Projection; user?: User.Projection; userEntityGrant?: UserEntityGrant.Projection; + userRelation?: UserRelation.Projection; } & Partial>; declare type OperEntityIdProjection = OneOf<{ id: number; @@ -56,12 +72,24 @@ declare type OperEntityIdProjection = OneOf<{ declare type OperIdProjection = OneOf<{ operId: number; }>; +declare type ActionAuthIdProjection = OneOf<{ + entityId: number; +}>; +declare type RelationIdProjection = OneOf<{ + entityId: number; +}>; +declare type RelationAuthIdProjection = OneOf<{ + entityId: number; +}>; declare type UserIdProjection = OneOf<{ entityId: number; }>; declare type UserEntityGrantIdProjection = OneOf<{ entityId: number; }>; +declare type UserRelationIdProjection = OneOf<{ + entityId: number; +}>; export declare type SortAttr = { id: number; } | { @@ -78,10 +106,18 @@ export declare type SortAttr = { entity: number; } | { entityId: number; +} | { + actionAuth: ActionAuth.SortAttr; +} | { + relation: Relation.SortAttr; +} | { + relationAuth: RelationAuth.SortAttr; } | { user: User.SortAttr; } | { userEntityGrant: UserEntityGrant.SortAttr; +} | { + userRelation: UserRelation.SortAttr; } | { [k: string]: any; } | OneOf>; @@ -99,6 +135,39 @@ export declare type CreateOperationData = FormCreateData; })) & ({ + entity?: never; + entityId?: never; + actionAuth: ActionAuth.CreateSingleOperation; +} | { + entity: "actionAuth"; + entityId: String<64>; + actionAuth: ActionAuth.UpdateOperation; +} | { + entity: "actionAuth"; + entityId: String<64>; +} | { + entity?: never; + entityId?: never; + relation: Relation.CreateSingleOperation; +} | { + entity: "relation"; + entityId: String<64>; + relation: Relation.UpdateOperation; +} | { + entity: "relation"; + entityId: String<64>; +} | { + entity?: never; + entityId?: never; + relationAuth: RelationAuth.CreateSingleOperation; +} | { + entity: "relationAuth"; + entityId: String<64>; + relationAuth: RelationAuth.UpdateOperation; +} | { + entity: "relationAuth"; + entityId: String<64>; +} | { entity?: never; entityId?: never; user: User.CreateSingleOperation; @@ -120,6 +189,17 @@ export declare type CreateOperationData = FormCreateData; +} | { + entity?: never; + entityId?: never; + userRelation: UserRelation.CreateSingleOperation; +} | { + entity: "userRelation"; + entityId: String<64>; + userRelation: UserRelation.UpdateOperation; +} | { + entity: "userRelation"; + entityId: String<64>; } | { entity?: string; entityId?: string; @@ -135,6 +215,18 @@ export declare type UpdateOperationData = FormUpdateData | null; })) & ({ + actionAuth?: ActionAuth.CreateSingleOperation | ActionAuth.UpdateOperation | ActionAuth.RemoveOperation; + entityId?: never; + entity?: never; +} | { + relation?: Relation.CreateSingleOperation | Relation.UpdateOperation | Relation.RemoveOperation; + entityId?: never; + entity?: never; +} | { + relationAuth?: RelationAuth.CreateSingleOperation | RelationAuth.UpdateOperation | RelationAuth.RemoveOperation; + entityId?: never; + entity?: never; +} | { user?: User.CreateSingleOperation | User.UpdateOperation | User.RemoveOperation; entityId?: never; entity?: never; @@ -143,24 +235,40 @@ export declare type UpdateOperationData = FormUpdateData | null; }) & { [k: string]: any; }; export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; export declare type RemoveOperationData = {} & ({ + actionAuth?: ActionAuth.UpdateOperation | ActionAuth.RemoveOperation; +} | { + relation?: Relation.UpdateOperation | Relation.RemoveOperation; +} | { + relationAuth?: RelationAuth.UpdateOperation | RelationAuth.RemoveOperation; +} | { user?: User.UpdateOperation | User.RemoveOperation; } | { userEntityGrant?: UserEntityGrant.UpdateOperation | UserEntityGrant.RemoveOperation; +} | { + userRelation?: UserRelation.UpdateOperation | UserRelation.RemoveOperation; } | { [k: string]: any; }); export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; export declare type OperIdSubQuery = Selection; +export declare type ActionAuthIdSubQuery = Selection; +export declare type RelationIdSubQuery = Selection; +export declare type RelationAuthIdSubQuery = Selection; export declare type UserIdSubQuery = Selection; export declare type UserEntityGrantIdSubQuery = Selection; +export declare type UserRelationIdSubQuery = Selection; export declare type OperEntityIdSubQuery = Selection; export declare type EntityDef = { Schema: Schema; diff --git a/lib/base-app-domain/OperEntity/Storage.js b/lib/base-app-domain/OperEntity/Storage.js index 3421012..f69e6f2 100644 --- a/lib/base-app-domain/OperEntity/Storage.js +++ b/lib/base-app-domain/OperEntity/Storage.js @@ -15,7 +15,7 @@ exports.desc = { params: { length: 32 }, - ref: ["user", "userEntityGrant"] + ref: ["actionAuth", "relation", "relationAuth", "user", "userEntityGrant", "userRelation"] }, entityId: { notNull: true, diff --git a/lib/base-app-domain/Relation/Schema.d.ts b/lib/base-app-domain/Relation/Schema.d.ts new file mode 100644 index 0000000..fbf5182 --- /dev/null +++ b/lib/base-app-domain/Relation/Schema.d.ts @@ -0,0 +1,160 @@ +import { String } from "../../types/DataType"; +import { Q_DateValue, Q_StringValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "../../types/Demand"; +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 ActionAuth from "../ActionAuth/Schema"; +import * as RelationAuth from "../RelationAuth/Schema"; +import * as UserRelation from "../UserRelation/Schema"; +import * as ModiEntity from "../ModiEntity/Schema"; +import * as OperEntity from "../OperEntity/Schema"; +export declare type OpSchema = EntityShape & { + entity: String<32>; + entityId: String<64>; + name: String<32>; + display: String<32>; +}; +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { + entity: String<32>; + entityId: String<64>; + name: String<32>; + display: String<32>; + actionAuth$relation?: Array; + actionAuth$relation$$aggr?: AggregationResult; + relationAuth$relation?: Array; + relationAuth$relation$$aggr?: AggregationResult; + userRelation$relation?: Array; + userRelation$relation$$aggr?: AggregationResult; + modiEntity$entity?: Array; + modiEntity$entity$$aggr?: AggregationResult; + operEntity$entity?: Array; + operEntity$entity$$aggr?: AggregationResult; +} & { + [A in ExpressionKey]?: any; +}; +declare type AttrFilter = { + id: Q_StringValue | SubQuery.RelationIdSubQuery; + $$createAt$$: Q_DateValue; + $$seq$$: Q_StringValue; + $$updateAt$$: Q_DateValue; + entity: Q_StringValue; + entityId: Q_StringValue; + name: Q_StringValue; + display: Q_StringValue; +}; +export declare type Filter = MakeFilter>; +export declare type Projection = { + "#id"?: NodeId; + [k: string]: any; + id?: number; + $$createAt$$?: number; + $$updateAt$$?: number; + $$seq$$?: number; + entity?: number; + entityId?: number; + name?: number; + display?: number; + actionAuth$relation?: ActionAuth.Selection & { + $entity: "actionAuth"; + }; + actionAuth$relation$$aggr?: ActionAuth.Aggregation & { + $entity: "actionAuth"; + }; + relationAuth$relation?: RelationAuth.Selection & { + $entity: "relationAuth"; + }; + relationAuth$relation$$aggr?: RelationAuth.Aggregation & { + $entity: "relationAuth"; + }; + userRelation$relation?: UserRelation.Selection & { + $entity: "userRelation"; + }; + userRelation$relation$$aggr?: UserRelation.Aggregation & { + $entity: "userRelation"; + }; + 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>; +declare type RelationIdProjection = OneOf<{ + id: number; +}>; +export declare type SortAttr = { + id: number; +} | { + $$createAt$$: number; +} | { + $$seq$$: number; +} | { + $$updateAt$$: number; +} | { + entity: number; +} | { + entityId: number; +} | { + name: number; +} | { + display: number; +} | { + [k: string]: any; +} | OneOf>; +export declare type SortNode = { + $attr: SortAttr; + $direction?: "asc" | "desc"; +}; +export declare type Sorter = SortNode[]; +export declare type SelectOperation

= OakSelection<"select", P, Filter, Sorter>; +export declare type Selection

= Omit, "action">; +export declare type Aggregation = DeduceAggregation; +export declare type CreateOperationData = FormCreateData> & ({ + entity?: string; + entityId?: string; + [K: string]: any; +}) & { + actionAuth$relation?: OakOperation, ActionAuth.Filter> | OakOperation<"create", Omit[]> | Array> | OakOperation, ActionAuth.Filter>>; + relationAuth$relation?: OakOperation, RelationAuth.Filter> | OakOperation<"create", Omit[]> | Array> | OakOperation, RelationAuth.Filter>>; + userRelation$relation?: OakOperation, UserRelation.Filter> | OakOperation<"create", Omit[]> | Array> | OakOperation, UserRelation.Filter>>; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>; +export declare type CreateMultipleOperation = OakOperation<"create", Array>; +export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation; +export declare type UpdateOperationData = FormUpdateData & { + [k: string]: any; + actionAuth$relation?: ActionAuth.UpdateOperation | ActionAuth.RemoveOperation | OakOperation<"create", Omit[]> | Array> | ActionAuth.UpdateOperation | ActionAuth.RemoveOperation>; + relationAuth$relation?: RelationAuth.UpdateOperation | RelationAuth.RemoveOperation | OakOperation<"create", Omit[]> | Array> | RelationAuth.UpdateOperation | RelationAuth.RemoveOperation>; + userRelation$relation?: UserRelation.UpdateOperation | UserRelation.RemoveOperation | OakOperation<"create", Omit[]> | Array> | UserRelation.UpdateOperation | UserRelation.RemoveOperation>; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {}; +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type RelationIdSubQuery = Selection; +export declare type EntityDef = { + Schema: Schema; + OpSchema: OpSchema; + Action: OakMakeAction | string; + Selection: Selection; + Aggregation: Aggregation; + Operation: Operation; + Create: CreateOperation; + Update: UpdateOperation; + Remove: RemoveOperation; + CreateSingle: CreateSingleOperation; + CreateMulti: CreateMultipleOperation; +}; +export {}; diff --git a/lib/base-app-domain/Relation/Schema.js b/lib/base-app-domain/Relation/Schema.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/base-app-domain/Relation/Schema.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/base-app-domain/Relation/Storage.d.ts b/lib/base-app-domain/Relation/Storage.d.ts new file mode 100644 index 0000000..606c158 --- /dev/null +++ b/lib/base-app-domain/Relation/Storage.d.ts @@ -0,0 +1,3 @@ +import { StorageDesc } from "../../types/Storage"; +import { OpSchema } from "./Schema"; +export declare const desc: StorageDesc; diff --git a/lib/base-app-domain/Relation/Storage.js b/lib/base-app-domain/Relation/Storage.js new file mode 100644 index 0000000..cc91eb0 --- /dev/null +++ b/lib/base-app-domain/Relation/Storage.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.desc = void 0; +var action_1 = require("../../actions/action"); +exports.desc = { + attributes: { + entity: { + notNull: true, + type: "varchar", + params: { + length: 32 + } + }, + entityId: { + notNull: true, + type: "varchar", + params: { + length: 64 + } + }, + name: { + notNull: true, + type: "varchar", + params: { + length: 32 + } + }, + display: { + notNull: true, + type: "varchar", + params: { + length: 32 + } + } + }, + actionType: "crud", + actions: action_1.genericActions, + indexes: [ + { + name: 'index_entity_entityId_name', + attributes: [ + { + name: 'entity' + }, + { + name: 'entityId' + }, + { + name: 'name' + } + ], + config: { + unique: true + } + } + ] +}; diff --git a/lib/base-app-domain/RelationAuth/Schema.d.ts b/lib/base-app-domain/RelationAuth/Schema.d.ts new file mode 100644 index 0000000..447ab6d --- /dev/null +++ b/lib/base-app-domain/RelationAuth/Schema.d.ts @@ -0,0 +1,156 @@ +import { String, ForeignKey } from "../../types/DataType"; +import { Q_DateValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "../../types/Demand"; +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 Relation from "../Relation/Schema"; +import * as ModiEntity from "../ModiEntity/Schema"; +import * as OperEntity from "../OperEntity/Schema"; +declare type Relations = string[]; +export declare type OpSchema = EntityShape & { + relationId: ForeignKey<"relation">; + path: String<256>; + destEntity: String<32>; + deRelations: Relations; +}; +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { + relationId: ForeignKey<"relation">; + path: String<256>; + destEntity: String<32>; + deRelations: Relations; + relation: Relation.Schema; + modiEntity$entity?: Array; + modiEntity$entity$$aggr?: AggregationResult; + operEntity$entity?: Array; + operEntity$entity$$aggr?: AggregationResult; +} & { + [A in ExpressionKey]?: any; +}; +declare type AttrFilter = { + id: Q_StringValue | SubQuery.RelationAuthIdSubQuery; + $$createAt$$: Q_DateValue; + $$seq$$: Q_StringValue; + $$updateAt$$: Q_DateValue; + relationId: Q_StringValue | SubQuery.RelationIdSubQuery; + relation: Relation.Filter; + path: Q_StringValue; + destEntity: Q_StringValue; + deRelations: Q_EnumValue; +}; +export declare type Filter = MakeFilter>; +export declare type Projection = { + "#id"?: NodeId; + [k: string]: any; + id?: number; + $$createAt$$?: number; + $$updateAt$$?: number; + $$seq$$?: number; + relationId?: number; + relation?: Relation.Projection; + path?: number; + destEntity?: number; + deRelations?: number; + 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>; +declare type RelationAuthIdProjection = OneOf<{ + id: number; +}>; +declare type RelationIdProjection = OneOf<{ + relationId: number; +}>; +export declare type SortAttr = { + id: number; +} | { + $$createAt$$: number; +} | { + $$seq$$: number; +} | { + $$updateAt$$: number; +} | { + relationId: number; +} | { + relation: Relation.SortAttr; +} | { + path: number; +} | { + destEntity: number; +} | { + deRelations: number; +} | { + [k: string]: any; +} | OneOf>; +export declare type SortNode = { + $attr: SortAttr; + $direction?: "asc" | "desc"; +}; +export declare type Sorter = SortNode[]; +export declare type SelectOperation

= OakSelection<"select", P, Filter, Sorter>; +export declare type Selection

= Omit, "action">; +export declare type Aggregation = DeduceAggregation; +export declare type CreateOperationData = FormCreateData> & (({ + relationId?: never; + relation: Relation.CreateSingleOperation; +} | { + relationId: String<64>; + relation?: Relation.UpdateOperation; +} | { + relationId: String<64>; +})) & { + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>; +export declare type CreateMultipleOperation = OakOperation<"create", Array>; +export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation; +export declare type UpdateOperationData = FormUpdateData> & (({ + relation: Relation.CreateSingleOperation; + relationId?: never; +} | { + relation: Relation.UpdateOperation; + relationId?: never; +} | { + relation: Relation.RemoveOperation; + relationId?: never; +} | { + relation?: never; + relationId?: String<64> | null; +})) & { + [k: string]: any; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & (({ + relation?: Relation.UpdateOperation | Relation.RemoveOperation; +})); +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type RelationIdSubQuery = Selection; +export declare type RelationAuthIdSubQuery = Selection; +export declare type EntityDef = { + Schema: Schema; + OpSchema: OpSchema; + Action: OakMakeAction | string; + Selection: Selection; + Aggregation: Aggregation; + Operation: Operation; + Create: CreateOperation; + Update: UpdateOperation; + Remove: RemoveOperation; + CreateSingle: CreateSingleOperation; + CreateMulti: CreateMultipleOperation; +}; +export {}; diff --git a/lib/base-app-domain/RelationAuth/Schema.js b/lib/base-app-domain/RelationAuth/Schema.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/base-app-domain/RelationAuth/Schema.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/base-app-domain/RelationAuth/Storage.d.ts b/lib/base-app-domain/RelationAuth/Storage.d.ts new file mode 100644 index 0000000..606c158 --- /dev/null +++ b/lib/base-app-domain/RelationAuth/Storage.d.ts @@ -0,0 +1,3 @@ +import { StorageDesc } from "../../types/Storage"; +import { OpSchema } from "./Schema"; +export declare const desc: StorageDesc; diff --git a/lib/base-app-domain/RelationAuth/Storage.js b/lib/base-app-domain/RelationAuth/Storage.js new file mode 100644 index 0000000..70e8aea --- /dev/null +++ b/lib/base-app-domain/RelationAuth/Storage.js @@ -0,0 +1,49 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.desc = void 0; +var action_1 = require("../../actions/action"); +exports.desc = { + attributes: { + relationId: { + notNull: true, + type: "ref", + ref: "relation" + }, + path: { + notNull: true, + type: "varchar", + params: { + length: 256 + } + }, + destEntity: { + notNull: true, + type: "varchar", + params: { + length: 32 + } + }, + deRelations: { + notNull: true, + type: "object" + } + }, + actionType: "crud", + actions: action_1.genericActions, + indexes: [ + { + name: 'index_relation_path', + attributes: [ + { + name: "relationId" + }, + { + name: 'path' + }, + ], + config: { + unique: true + } + } + ] +}; diff --git a/lib/base-app-domain/Storage.js b/lib/base-app-domain/Storage.js index a08efa8..ebcd85e 100644 --- a/lib/base-app-domain/Storage.js +++ b/lib/base-app-domain/Storage.js @@ -1,17 +1,25 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.storageSchema = void 0; -var Storage_1 = require("./Modi/Storage"); -var Storage_2 = require("./ModiEntity/Storage"); -var Storage_3 = require("./Oper/Storage"); -var Storage_4 = require("./OperEntity/Storage"); -var Storage_5 = require("./User/Storage"); -var Storage_6 = require("./UserEntityGrant/Storage"); +var Storage_1 = require("./ActionAuth/Storage"); +var Storage_2 = require("./Modi/Storage"); +var Storage_3 = require("./ModiEntity/Storage"); +var Storage_4 = require("./Oper/Storage"); +var Storage_5 = require("./OperEntity/Storage"); +var Storage_6 = require("./Relation/Storage"); +var Storage_7 = require("./RelationAuth/Storage"); +var Storage_8 = require("./User/Storage"); +var Storage_9 = require("./UserEntityGrant/Storage"); +var Storage_10 = require("./UserRelation/Storage"); exports.storageSchema = { - modi: Storage_1.desc, - modiEntity: Storage_2.desc, - oper: Storage_3.desc, - operEntity: Storage_4.desc, - user: Storage_5.desc, - userEntityGrant: Storage_6.desc + actionAuth: Storage_1.desc, + modi: Storage_2.desc, + modiEntity: Storage_3.desc, + oper: Storage_4.desc, + operEntity: Storage_5.desc, + relation: Storage_6.desc, + relationAuth: Storage_7.desc, + user: Storage_8.desc, + userEntityGrant: Storage_9.desc, + userRelation: Storage_10.desc }; diff --git a/lib/base-app-domain/User/Schema.d.ts b/lib/base-app-domain/User/Schema.d.ts index 74435ac..ce598a7 100644 --- a/lib/base-app-domain/User/Schema.d.ts +++ b/lib/base-app-domain/User/Schema.d.ts @@ -6,6 +6,7 @@ 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 UserRelation from "../UserRelation/Schema"; import * as ModiEntity from "../ModiEntity/Schema"; import * as OperEntity from "../OperEntity/Schema"; export declare type OpSchema = EntityShape & { @@ -27,6 +28,8 @@ export declare type Schema = EntityShape & { oper$operator$$aggr?: AggregationResult; user$ref?: Array; user$ref$$aggr?: AggregationResult; + userRelation$user?: Array; + userRelation$user$$aggr?: AggregationResult; modiEntity$entity?: Array; modiEntity$entity$$aggr?: AggregationResult; operEntity$entity?: Array; @@ -72,6 +75,12 @@ export declare type Projection = { user$ref$$aggr?: Aggregation & { $entity: "user"; }; + userRelation$user?: UserRelation.Selection & { + $entity: "userRelation"; + }; + userRelation$user$$aggr?: UserRelation.Aggregation & { + $entity: "userRelation"; + }; modiEntity$entity?: ModiEntity.Selection & { $entity: "modiEntity"; }; @@ -131,6 +140,7 @@ export declare type CreateOperationData = FormCreateData })) & { oper$operator?: OakOperation<"create", Omit[]> | Array>>; user$ref?: OakOperation, Filter> | OakOperation<"create", Omit[]> | Array> | OakOperation, Filter>>; + userRelation$user?: OakOperation, UserRelation.Filter> | OakOperation<"create", Omit[]> | Array> | OakOperation, UserRelation.Filter>>; modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; @@ -153,6 +163,7 @@ export declare type UpdateOperationData = FormUpdateData [k: string]: any; oper$operator?: OakOperation<"create", Omit[]> | Array>>; user$ref?: UpdateOperation | RemoveOperation | OakOperation<"create", Omit[]> | Array> | UpdateOperation | RemoveOperation>; + userRelation$user?: UserRelation.UpdateOperation | UserRelation.RemoveOperation | OakOperation<"create", Omit[]> | Array> | UserRelation.UpdateOperation | UserRelation.RemoveOperation>; modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; diff --git a/lib/base-app-domain/UserRelation/Schema.d.ts b/lib/base-app-domain/UserRelation/Schema.d.ts new file mode 100644 index 0000000..319fa71 --- /dev/null +++ b/lib/base-app-domain/UserRelation/Schema.d.ts @@ -0,0 +1,175 @@ +import { String, ForeignKey } from "../../types/DataType"; +import { Q_DateValue, Q_StringValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "../../types/Demand"; +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 User from "../User/Schema"; +import * as Relation from "../Relation/Schema"; +import * as ModiEntity from "../ModiEntity/Schema"; +import * as OperEntity from "../OperEntity/Schema"; +export declare type OpSchema = EntityShape & { + userId: ForeignKey<"user">; + relationId: ForeignKey<"relation">; +}; +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { + userId: ForeignKey<"user">; + relationId: ForeignKey<"relation">; + user: User.Schema; + relation: Relation.Schema; + modiEntity$entity?: Array; + modiEntity$entity$$aggr?: AggregationResult; + operEntity$entity?: Array; + operEntity$entity$$aggr?: AggregationResult; +} & { + [A in ExpressionKey]?: any; +}; +declare type AttrFilter = { + id: Q_StringValue | SubQuery.UserRelationIdSubQuery; + $$createAt$$: Q_DateValue; + $$seq$$: Q_StringValue; + $$updateAt$$: Q_DateValue; + userId: Q_StringValue | SubQuery.UserIdSubQuery; + user: User.Filter; + relationId: Q_StringValue | SubQuery.RelationIdSubQuery; + relation: Relation.Filter; +}; +export declare type Filter = MakeFilter>; +export declare type Projection = { + "#id"?: NodeId; + [k: string]: any; + id?: number; + $$createAt$$?: number; + $$updateAt$$?: number; + $$seq$$?: number; + userId?: number; + user?: User.Projection; + relationId?: number; + relation?: Relation.Projection; + 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>; +declare type UserRelationIdProjection = OneOf<{ + id: number; +}>; +declare type UserIdProjection = OneOf<{ + userId: number; +}>; +declare type RelationIdProjection = OneOf<{ + relationId: number; +}>; +export declare type SortAttr = { + id: number; +} | { + $$createAt$$: number; +} | { + $$seq$$: number; +} | { + $$updateAt$$: number; +} | { + userId: number; +} | { + user: User.SortAttr; +} | { + relationId: number; +} | { + relation: Relation.SortAttr; +} | { + [k: string]: any; +} | OneOf>; +export declare type SortNode = { + $attr: SortAttr; + $direction?: "asc" | "desc"; +}; +export declare type Sorter = SortNode[]; +export declare type SelectOperation

= OakSelection<"select", P, Filter, Sorter>; +export declare type Selection

= Omit, "action">; +export declare type Aggregation = DeduceAggregation; +export declare type CreateOperationData = FormCreateData> & (({ + userId?: never; + user: User.CreateSingleOperation; +} | { + userId: String<64>; + user?: User.UpdateOperation; +} | { + userId: String<64>; +}) & ({ + relationId?: never; + relation: Relation.CreateSingleOperation; +} | { + relationId: String<64>; + relation?: Relation.UpdateOperation; +} | { + relationId: String<64>; +})) & { + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>; +export declare type CreateMultipleOperation = OakOperation<"create", Array>; +export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation; +export declare type UpdateOperationData = FormUpdateData> & (({ + user: User.CreateSingleOperation; + userId?: never; +} | { + user: User.UpdateOperation; + userId?: never; +} | { + user: User.RemoveOperation; + userId?: never; +} | { + user?: never; + userId?: String<64> | null; +}) & ({ + relation: Relation.CreateSingleOperation; + relationId?: never; +} | { + relation: Relation.UpdateOperation; + relationId?: never; +} | { + relation: Relation.RemoveOperation; + relationId?: never; +} | { + relation?: never; + relationId?: String<64> | null; +})) & { + [k: string]: any; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; +}; +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & (({ + user?: User.UpdateOperation | User.RemoveOperation; +}) & ({ + relation?: Relation.UpdateOperation | Relation.RemoveOperation; +})); +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type UserIdSubQuery = Selection; +export declare type RelationIdSubQuery = Selection; +export declare type UserRelationIdSubQuery = Selection; +export declare type EntityDef = { + Schema: Schema; + OpSchema: OpSchema; + Action: OakMakeAction | string; + Selection: Selection; + Aggregation: Aggregation; + Operation: Operation; + Create: CreateOperation; + Update: UpdateOperation; + Remove: RemoveOperation; + CreateSingle: CreateSingleOperation; + CreateMulti: CreateMultipleOperation; +}; +export {}; diff --git a/lib/base-app-domain/UserRelation/Schema.js b/lib/base-app-domain/UserRelation/Schema.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/lib/base-app-domain/UserRelation/Schema.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/base-app-domain/UserRelation/Storage.d.ts b/lib/base-app-domain/UserRelation/Storage.d.ts new file mode 100644 index 0000000..606c158 --- /dev/null +++ b/lib/base-app-domain/UserRelation/Storage.d.ts @@ -0,0 +1,3 @@ +import { StorageDesc } from "../../types/Storage"; +import { OpSchema } from "./Schema"; +export declare const desc: StorageDesc; diff --git a/lib/base-app-domain/UserRelation/Storage.js b/lib/base-app-domain/UserRelation/Storage.js new file mode 100644 index 0000000..ce60b3e --- /dev/null +++ b/lib/base-app-domain/UserRelation/Storage.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.desc = void 0; +var action_1 = require("../../actions/action"); +exports.desc = { + attributes: { + userId: { + notNull: true, + type: "ref", + ref: "user" + }, + relationId: { + notNull: true, + type: "ref", + ref: "relation" + } + }, + actionType: "crud", + actions: action_1.genericActions, + indexes: [ + { + name: 'index_user_relation', + attributes: [ + { + name: "userId" + }, + { + name: "relationId" + }, + ], + config: { + unique: true + } + } + ] +}; diff --git a/lib/base-app-domain/_SubQuery.d.ts b/lib/base-app-domain/_SubQuery.d.ts index 9394204..d69d357 100644 --- a/lib/base-app-domain/_SubQuery.d.ts +++ b/lib/base-app-domain/_SubQuery.d.ts @@ -1,9 +1,18 @@ +import * as ActionAuth from "./ActionAuth/Schema"; import * as Modi from "./Modi/Schema"; import * as ModiEntity from "./ModiEntity/Schema"; import * as Oper from "./Oper/Schema"; import * as OperEntity from "./OperEntity/Schema"; +import * as Relation from "./Relation/Schema"; +import * as RelationAuth from "./RelationAuth/Schema"; import * as User from "./User/Schema"; import * as UserEntityGrant from "./UserEntityGrant/Schema"; +import * as UserRelation from "./UserRelation/Schema"; +export declare type ActionAuthIdSubQuery = { + [K in "$in" | "$nin"]?: (ActionAuth.ActionAuthIdSubQuery & { + entity: "actionAuth"; + }) | any; +}; export declare type ModiIdSubQuery = { [K in "$in" | "$nin"]?: (ModiEntity.ModiIdSubQuery & { entity: "modiEntity"; @@ -28,11 +37,29 @@ export declare type OperEntityIdSubQuery = { entity: "operEntity"; }) | any; }; +export declare type RelationIdSubQuery = { + [K in "$in" | "$nin"]?: (ActionAuth.RelationIdSubQuery & { + entity: "actionAuth"; + }) | (RelationAuth.RelationIdSubQuery & { + entity: "relationAuth"; + }) | (UserRelation.RelationIdSubQuery & { + entity: "userRelation"; + }) | (Relation.RelationIdSubQuery & { + entity: "relation"; + }) | any; +}; +export declare type RelationAuthIdSubQuery = { + [K in "$in" | "$nin"]?: (RelationAuth.RelationAuthIdSubQuery & { + entity: "relationAuth"; + }) | any; +}; export declare type UserIdSubQuery = { [K in "$in" | "$nin"]?: (Oper.UserIdSubQuery & { entity: "oper"; }) | (User.UserIdSubQuery & { entity: "user"; + }) | (UserRelation.UserIdSubQuery & { + entity: "userRelation"; }) | (User.UserIdSubQuery & { entity: "user"; }) | any; @@ -42,3 +69,8 @@ export declare type UserEntityGrantIdSubQuery = { entity: "userEntityGrant"; }) | any; }; +export declare type UserRelationIdSubQuery = { + [K in "$in" | "$nin"]?: (UserRelation.UserRelationIdSubQuery & { + entity: "userRelation"; + }) | any; +}; diff --git a/lib/entities/ActionAuth.d.ts b/lib/entities/ActionAuth.d.ts new file mode 100644 index 0000000..c57a16c --- /dev/null +++ b/lib/entities/ActionAuth.d.ts @@ -0,0 +1,11 @@ +import { String } from '../types/DataType'; +import { EntityShape } from '../types/Entity'; +import { Schema as Relation } from './Relation'; +declare type Actions = string[]; +export interface Schema extends EntityShape { + relation: Relation; + path: String<256>; + destEntity: String<32>; + deActions: Actions; +} +export {}; diff --git a/lib/entities/ActionAuth.js b/lib/entities/ActionAuth.js new file mode 100644 index 0000000..cc1cce8 --- /dev/null +++ b/lib/entities/ActionAuth.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +; +var indexes = [ + { + name: 'index_relation_path', + attributes: [ + { + name: 'relation', + }, + { + name: 'path', + }, + ], + config: { + unique: true, + }, + }, +]; +var locale = { + zh_CN: { + name: '用户授权', + attr: { + relation: '关系', + path: '路径', + destEntity: '目标对象', + deActions: '目标对象动作', + }, + }, +}; diff --git a/lib/entities/Relation.d.ts b/lib/entities/Relation.d.ts new file mode 100644 index 0000000..c6e9f14 --- /dev/null +++ b/lib/entities/Relation.d.ts @@ -0,0 +1,8 @@ +import { String } from '../types/DataType'; +import { EntityShape } from '../types/Entity'; +export interface Schema extends EntityShape { + entity: String<32>; + entityId: String<64>; + name: String<32>; + display: String<32>; +} diff --git a/lib/entities/Relation.js b/lib/entities/Relation.js new file mode 100644 index 0000000..2e988c5 --- /dev/null +++ b/lib/entities/Relation.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +; +var indexes = [ + { + name: 'index_entity_entityId_name', + attributes: [ + { + name: 'entity', + }, + { + name: 'entityId', + }, + { + name: 'name', + } + ], + config: { + unique: true, + }, + }, +]; +var locale = { + zh_CN: { + name: '用户授权', + attr: { + name: '关系', + entity: '目标对象', + entityId: '目标对象id', + display: '显示值', + }, + }, +}; diff --git a/lib/entities/RelationAuth.d.ts b/lib/entities/RelationAuth.d.ts new file mode 100644 index 0000000..dc79db5 --- /dev/null +++ b/lib/entities/RelationAuth.d.ts @@ -0,0 +1,11 @@ +import { String } from '../types/DataType'; +import { EntityShape } from '../types/Entity'; +import { Schema as Relation } from './Relation'; +declare type Relations = string[]; +export interface Schema extends EntityShape { + relation: Relation; + path: String<256>; + destEntity: String<32>; + deRelations: Relations; +} +export {}; diff --git a/lib/entities/RelationAuth.js b/lib/entities/RelationAuth.js new file mode 100644 index 0000000..2b59d69 --- /dev/null +++ b/lib/entities/RelationAuth.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +; +var indexes = [ + { + name: 'index_relation_path', + attributes: [ + { + name: 'relation', + }, + { + name: 'path', + }, + ], + config: { + unique: true, + }, + }, +]; +var locale = { + zh_CN: { + name: '用户授权', + attr: { + relation: '关系', + path: '路径', + destEntity: '目标对象', + deRelations: '目标对象关系', + }, + }, +}; diff --git a/lib/entities/UserRelation.d.ts b/lib/entities/UserRelation.d.ts new file mode 100644 index 0000000..8670880 --- /dev/null +++ b/lib/entities/UserRelation.d.ts @@ -0,0 +1,7 @@ +import { EntityShape } from '../types/Entity'; +import { Schema as User } from './User'; +import { Schema as Relation } from './Relation'; +export interface Schema extends EntityShape { + user: User; + relation: Relation; +} diff --git a/lib/entities/UserRelation.js b/lib/entities/UserRelation.js new file mode 100644 index 0000000..709bfa3 --- /dev/null +++ b/lib/entities/UserRelation.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +; +var indexes = [ + { + name: 'index_user_relation', + attributes: [ + { + name: 'user', + }, + { + name: 'relation', + }, + ], + config: { + unique: true, + }, + }, +]; +var locale = { + zh_CN: { + name: '用户对象关系', + attr: { + user: '关系', + relation: '目标关系', + }, + }, +}; diff --git a/lib/store/checker.js b/lib/store/checker.js index 5c95ce9..2190560 100644 --- a/lib/store/checker.js +++ b/lib/store/checker.js @@ -113,7 +113,7 @@ function translateCheckerInAsyncContext(checker) { var fn = (function (_a, context, option) { var operation = _a.operation; return tslib_1.__awaiter(_this, void 0, void 0, function () { - var result, _b, filter, action; + var result, _b, filter, action, errMsg2; return tslib_1.__generator(this, function (_c) { switch (_c.label) { case 0: @@ -146,7 +146,8 @@ function translateCheckerInAsyncContext(checker) { if (_c.sent()) { return [2 /*return*/]; } - throw new Exception_1.OakUserUnpermittedException(errMsg_2); + errMsg2 = typeof errMsg_2 === 'function' ? errMsg_2(operation, context, option) : errMsg_2; + throw new Exception_1.OakUserUnpermittedException(errMsg2); case 5: return [2 /*return*/, 0]; } }); @@ -236,7 +237,8 @@ function translateCheckerInSyncContext(checker) { if ((0, filter_1.checkFilterContains)(entity, context, result, filter, true)) { return; } - throw new Exception_1.OakUserUnpermittedException(errMsg_4); + var errMsg2 = typeof errMsg_4 === 'function' ? errMsg_4(operation, context, option) : errMsg_4; + throw new Exception_1.OakUserUnpermittedException(errMsg2); } }; return { @@ -784,7 +786,10 @@ function createAuthCheckers(schema, authDict) { var filter = makePotentialFilter(operation, context, raFilterMakerDict_1[relation]); return filter; }, - errMsg: '越权操作', + errMsg: function (operation, context) { + console.error("\u521B\u5EFA".concat(entity, "\u65F6\u8D8A\u6743\uFF0CuserId\u662F").concat(context.getCurrentUserId(), "\uFF0C\u6570\u636E\u662F").concat(JSON.stringify(operation.data))); + return "\u521B\u5EFA".concat(entity, "\u65F6\u8D8A\u6743"); + }, }); checkers.push({ entity: userEntityName, @@ -842,7 +847,10 @@ function createAuthCheckers(schema, authDict) { } return makeFilterFromRows(toBeRemoved); */ }, - errMsg: '越权操作', + errMsg: function (operation, context) { + console.error("\u79FB\u9664".concat(entity, "\u65F6\u8D8A\u6743\uFF0CuserId\u662F").concat(context.getCurrentUserId(), "\uFF0C\u79FB\u9664\u6761\u4EF6\u662F").concat(JSON.stringify(operation.filter))); + return "\u79FB\u9664".concat(entity, "\u65F6\u8D8A\u6743"); + }, }); // 转让权限现在用update动作,只允许update userId给其它人 // todo 等实现的时候再写 @@ -859,7 +867,11 @@ function createAuthCheckers(schema, authDict) { var filter = makePotentialFilter(operation, context, filterMaker); return filter; }, - errMsg: '定义的actionAuth中检查出来越权操作', + errMsg: function (operation, context) { + var _a = operation, action = _a.action, data = _a.data, filter = _a.filter; + console.error("\u5BF9".concat(entity, "\u8FDB\u884C").concat(action, "\u65F6\u8D8A\u6743\uFF0CuserId\u662F").concat(context.getCurrentUserId(), "\n \u6570\u636E\u662F").concat(JSON.stringify(data), "\uFF0C\u6761\u4EF6\u662F").concat(JSON.stringify(filter))); + return "\u5BF9".concat(entity, "\u8FDB\u884C").concat(action, "\u65F6\u8D8A\u6743"); + }, }); }; for (var a in actionAuth) { diff --git a/lib/types/Auth.d.ts b/lib/types/Auth.d.ts index 6d83de8..2023c39 100644 --- a/lib/types/Auth.d.ts +++ b/lib/types/Auth.d.ts @@ -35,7 +35,7 @@ export declare type RelationChecker; relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync; - errMsg: string; + errMsg: string | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option?: OperateOption | SelectOption) => string); conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync); }; export declare type LogicalChecker | SyncContext> = { diff --git a/src/entities/ActionAuth.ts b/src/entities/ActionAuth.ts new file mode 100644 index 0000000..1bb7a1c --- /dev/null +++ b/src/entities/ActionAuth.ts @@ -0,0 +1,49 @@ +import { String } from '../types/DataType'; +import { LocaleDef } from '../types/Locale'; +import { EntityShape } from '../types/Entity'; +import { Index } from '../types/Storage'; +import { Schema as Relation } from './Relation'; + +type Actions = string[]; + +export interface Schema extends EntityShape { + relation: Relation; + path: String<256>; + destEntity: String<32>; + deActions: Actions; +}; + + +const indexes: Index[] = [ + { + name: 'index_relation_path', + attributes: [ + { + name: 'relation', + }, + { + name: 'path', + }, + ], + config: { + unique: true, + }, + }, +]; + +const locale: LocaleDef< + Schema, + '', + '', + {} +> = { + zh_CN: { + name: '用户授权', + attr: { + relation: '关系', + path: '路径', + destEntity: '目标对象', + deActions: '目标对象动作', + }, + }, +}; diff --git a/src/entities/Relation.ts b/src/entities/Relation.ts new file mode 100644 index 0000000..0d2dd45 --- /dev/null +++ b/src/entities/Relation.ts @@ -0,0 +1,49 @@ +import { String } from '../types/DataType'; +import { LocaleDef } from '../types/Locale'; +import { EntityShape } from '../types/Entity'; +import { Index } from '../types/Storage'; + +export interface Schema extends EntityShape { + entity: String<32>; + entityId: String<64>; // 可以为空 + name: String<32>; + display: String<32>; +}; + + +const indexes: Index[] = [ + { + name: 'index_entity_entityId_name', + attributes: [ + { + name: 'entity', + }, + { + name: 'entityId', + }, + { + name: 'name', + } + ], + config: { + unique: true, + }, + }, +]; + +const locale: LocaleDef< + Schema, + '', + '', + {} +> = { + zh_CN: { + name: '用户授权', + attr: { + name: '关系', + entity: '目标对象', + entityId: '目标对象id', + display: '显示值', + }, + }, +}; diff --git a/src/entities/RelationAuth.ts b/src/entities/RelationAuth.ts new file mode 100644 index 0000000..4f87e2b --- /dev/null +++ b/src/entities/RelationAuth.ts @@ -0,0 +1,49 @@ +import { String } from '../types/DataType'; +import { LocaleDef } from '../types/Locale'; +import { EntityShape } from '../types/Entity'; +import { Index } from '../types/Storage'; +import { Schema as Relation } from './Relation'; + +type Relations = string[]; + +export interface Schema extends EntityShape { + relation: Relation; + path: String<256>; + destEntity: String<32>; + deRelations: Relations; +}; + + +const indexes: Index[] = [ + { + name: 'index_relation_path', + attributes: [ + { + name: 'relation', + }, + { + name: 'path', + }, + ], + config: { + unique: true, + }, + }, +]; + +const locale: LocaleDef< + Schema, + '', + '', + {} +> = { + zh_CN: { + name: '用户授权', + attr: { + relation: '关系', + path: '路径', + destEntity: '目标对象', + deRelations: '目标对象关系', + }, + }, +}; diff --git a/src/entities/UserRelation.ts b/src/entities/UserRelation.ts new file mode 100644 index 0000000..bef0f66 --- /dev/null +++ b/src/entities/UserRelation.ts @@ -0,0 +1,44 @@ +import { String } from '../types/DataType'; +import { LocaleDef } from '../types/Locale'; +import { EntityShape } from '../types/Entity'; +import { Index } from '../types/Storage'; +import { Schema as User } from './User'; +import { Schema as Relation } from './Relation'; + +export interface Schema extends EntityShape { + user: User; + relation: Relation; +}; + + +const indexes: Index[] = [ + { + name: 'index_user_relation', + attributes: [ + { + name: 'user', + }, + { + name: 'relation', + }, + ], + config: { + unique: true, + }, + }, +]; + +const locale: LocaleDef< + Schema, + '', + '', + {} +> = { + zh_CN: { + name: '用户对象关系', + attr: { + user: '关系', + relation: '目标关系', + }, + }, +}; diff --git a/src/store/checker.ts b/src/store/checker.ts index 486f8fd..9d44656 100644 --- a/src/store/checker.ts +++ b/src/store/checker.ts @@ -115,7 +115,8 @@ export function translateCheckerInAsyncContext< if (await checkFilterContains(entity, context, result, filter, true)) { return; } - throw new OakUserUnpermittedException(errMsg); + const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg; + throw new OakUserUnpermittedException(errMsg2); } return 0; }) as UpdateTriggerInTxn['fn']; @@ -201,7 +202,8 @@ export function translateCheckerInSyncContext< if (checkFilterContains(entity, context, result as ED[T]['Selection']['filter'], filter, true)) { return; } - throw new OakUserUnpermittedException(errMsg); + const errMsg2 = typeof errMsg === 'function' ? errMsg(operation, context, option) : errMsg; + throw new OakUserUnpermittedException(errMsg2); } }; return { @@ -804,7 +806,10 @@ export function createAuthCheckers { + console.error(`创建${entity as string}时越权,userId是${context.getCurrentUserId()},数据是${JSON.stringify(operation.data)}`); + return `创建${entity as string}时越权`; + }, }); checkers.push({ @@ -863,7 +868,10 @@ export function createAuthCheckers { + console.error(`移除${entity as string}时越权,userId是${context.getCurrentUserId()},移除条件是${JSON.stringify(operation.filter)}`); + return `移除${entity as string}时越权`; + }, }); // 转让权限现在用update动作,只允许update userId给其它人 // todo 等实现的时候再写 @@ -881,7 +889,12 @@ export function createAuthCheckers { + const { action, data, filter } = operation as ED[keyof ED]['Create']; + console.error(`对${entity as string}进行${action}时越权,userId是${context.getCurrentUserId()} + 数据是${JSON.stringify(data)},条件是${JSON.stringify(filter)}`); + return `对${entity as string}进行${action}时越权`; + }, }); } } diff --git a/src/types/Auth.ts b/src/types/Auth.ts index 88ed328..f1650da 100644 --- a/src/types/Auth.ts +++ b/src/types/Auth.ts @@ -46,7 +46,7 @@ export type RelationChecker; relationFilter: (operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option: OperateOption | SelectOption) => SyncOrAsync, // 生成一个额外的relation相关的filter,加在原先的filter上 - errMsg: string; + errMsg: string | ((operation: ED[T]['Operation'] | ED[T]['Selection'], context: Cxt, option?: OperateOption | SelectOption) => string); conditionalFilter?: ED[T]['Update']['filter'] | ( (operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => SyncOrAsync ); diff --git a/tsconfig.json b/tsconfig.json index f7f9ad4..15e85a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -65,7 +65,7 @@ /* Advanced Options */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, - "include": ["src/**/*" ], + "include": ["src/**/*", "scripts/ActionAuth.ts222" ], "exclude": [ "node_modules", "**/*.spec.ts", From bb8b56f094f9a31a2c8e84f8c6e3a10b544a8a5d Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Mon, 17 Apr 2023 20:34:05 +0800 Subject: [PATCH 03/14] =?UTF-8?q?checker=E4=B8=AD=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E7=94=A8count,=E5=8F=AA=E8=83=BD=E7=94=A8select=EF=BC=8C?= =?UTF-8?q?=E5=90=A6=E5=88=99=E4=B8=8D=E4=BC=9A=E8=A7=A6=E5=8F=91reinforce?= =?UTF-8?q?Selection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/checker.js | 16 +++++++++++----- src/store/checker.ts | 18 ++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/store/checker.js b/lib/store/checker.js index 2190560..3ba3a22 100644 --- a/lib/store/checker.js +++ b/lib/store/checker.js @@ -596,19 +596,25 @@ function execCreateCounter(context, counter) { } else if (counter === null || counter === void 0 ? void 0 : counter.$entity) { var _a = counter, $entity = _a.$entity, $filter = _a.$filter, _b = _a.$count, $count_1 = _b === void 0 ? 1 : _b; - var count = context.count($entity, { + // count不走reinforceSelection,先用select + var result = context.select($entity, { + data: { + id: 1, + }, filter: $filter, + indexFrom: 0, + count: $count_1, }, { dontCollect: true }); - if (count instanceof Promise) { - return count.then(function (c2) { - if (c2 >= $count_1) { + if (result instanceof Promise) { + return result.then(function (r2) { + if (r2.length >= $count_1) { return undefined; } return new Exception_1.OakUserUnpermittedException(); }); } else { - return count >= $count_1 ? undefined : new Exception_1.OakUserUnpermittedException(); + return result.length >= $count_1 ? undefined : new Exception_1.OakUserUnpermittedException(); } } } diff --git a/src/store/checker.ts b/src/store/checker.ts index 9d44656..9ef04a0 100644 --- a/src/store/checker.ts +++ b/src/store/checker.ts @@ -623,13 +623,19 @@ function execCreateCounter>counter)?.$entity) { const { $entity, $filter, $count = 1 } = counter as CreateRelationSingleCounter; - const count = context.count($entity, { + // count不走reinforceSelection,先用select + const result = context.select($entity, { + data: { + id: 1, + }, filter: $filter, + indexFrom: 0, + count: $count, }, { dontCollect: true }); - if (count instanceof Promise) { - return count.then( - (c2) => { - if (c2 >= $count) { + if (result instanceof Promise) { + return result.then( + (r2) => { + if (r2.length >= $count) { return undefined; } return new OakUserUnpermittedException(); @@ -637,7 +643,7 @@ function execCreateCounter= $count ? undefined : new OakUserUnpermittedException(); + return result.length >= $count ? undefined : new OakUserUnpermittedException(); } } } From 87aa97065ab79fa1294b66781b2d76c122bbc1c0 Mon Sep 17 00:00:00 2001 From: wenjiarui Date: Mon, 24 Apr 2023 12:02:40 +0800 Subject: [PATCH 04/14] =?UTF-8?q?createCreateCheckers=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/checker.js | 2 +- src/store/checker.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/store/checker.js b/lib/store/checker.js index 3ba3a22..716d57b 100644 --- a/lib/store/checker.js +++ b/lib/store/checker.js @@ -1323,7 +1323,7 @@ function createCreateCheckers(schema) { continue; } } - else if (attr === 'entity' && attributes[attr].type === 'ref') { + else if (attr === 'entity' && attributes[attr].ref) { var hasCascadeCreate = false; try { for (var _c = (e_10 = void 0, tslib_1.__values(attributes[attr].ref)), _d = _c.next(); !_d.done; _d = _c.next()) { diff --git a/src/store/checker.ts b/src/store/checker.ts index 9ef04a0..fda57fa 100644 --- a/src/store/checker.ts +++ b/src/store/checker.ts @@ -1283,7 +1283,7 @@ export function createCreateCheckers Date: Mon, 24 Apr 2023 14:54:44 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=AF=94=E8=BE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/version.d.ts | 7 +++++++ lib/utils/version.js | 21 +++++++++++++++++++++ src/utils/version.ts | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 lib/utils/version.d.ts create mode 100644 lib/utils/version.js create mode 100644 src/utils/version.ts diff --git a/lib/utils/version.d.ts b/lib/utils/version.d.ts new file mode 100644 index 0000000..1486776 --- /dev/null +++ b/lib/utils/version.d.ts @@ -0,0 +1,7 @@ +/** + * 版本比较 + * @param curVersion 当前版本 + * @param reqVersion 比较版本 + * @returns + */ +export declare const compareVersion: (curVersion: string, reqVersion: string) => number; diff --git a/lib/utils/version.js b/lib/utils/version.js new file mode 100644 index 0000000..7c50264 --- /dev/null +++ b/lib/utils/version.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.compareVersion = void 0; +/** + * 版本比较 + * @param curVersion 当前版本 + * @param reqVersion 比较版本 + * @returns + */ +var compareVersion = function (curVersion, reqVersion) { + var v1 = curVersion.split('.'); + var v2 = reqVersion.split('.'); + for (var i = 0; i < Math.max(v1.length, v2.length); i++) { + var num1 = i < v1.length ? parseInt(v1[i], 10) : 0; + var num2 = i < v2.length ? parseInt(v2[i], 10) : 0; + if (num1 !== num2) + return num1 - num2; + } + return 0; +}; +exports.compareVersion = compareVersion; diff --git a/src/utils/version.ts b/src/utils/version.ts new file mode 100644 index 0000000..c1ebd62 --- /dev/null +++ b/src/utils/version.ts @@ -0,0 +1,18 @@ + +/** + * 版本比较 + * @param curVersion 当前版本 + * @param reqVersion 比较版本 + * @returns + */ +export const compareVersion = (curVersion: string, reqVersion: string): number => { + const v1 = curVersion.split('.'); + const v2 = reqVersion.split('.'); + + for (let i = 0; i < Math.max(v1.length, v2.length); i++) { + const num1 = i < v1.length ? parseInt(v1[i], 10) : 0; + const num2 = i < v2.length ? parseInt(v2[i], 10) : 0; + if (num1 !== num2) return num1 - num2; + } + return 0; +}; \ No newline at end of file From 952f65132bfedd22c7fb3dc1aac00036f83f1afb Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Sat, 6 May 2023 13:39:31 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E5=9C=A8cascadeSelect=E4=B8=AD,=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E5=89=8D=E5=8F=B0=E7=9A=84=E7=BA=A7=E8=81=94aggregate?= =?UTF-8?q?=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/CascadeStore.js | 300 ++++++++++++++++++------------------ src/store/CascadeStore.ts | 311 +++++++++++++++++++------------------- 2 files changed, 309 insertions(+), 302 deletions(-) diff --git a/lib/store/CascadeStore.js b/lib/store/CascadeStore.js index cf1216b..c03b206 100644 --- a/lib/store/CascadeStore.js +++ b/lib/store/CascadeStore.js @@ -9,6 +9,7 @@ var filter_1 = require("./filter"); var relation_1 = require("./relation"); var types_1 = require("../types"); var lodash_1 = require("../utils/lodash"); +var AsyncRowStore_1 = require("./AsyncRowStore"); var filter_2 = require("./filter"); var uuid_1 = require("../utils/uuid"); /**这个用来处理级联的select和update,对不同能力的 */ @@ -457,165 +458,168 @@ var CascadeStore = /** @class */ (function (_super) { var _g = projection2[attr], subProjection_1 = _g.data, subFilter_1 = _g.filter, indexFrom_1 = _g.indexFrom, count_1 = _g.count, subSorter_1 = _g.sorter; var _h = tslib_1.__read(relation, 2), entity2_1 = _h[0], foreignKey_1 = _h[1]; var isAggr = attr.endsWith('$$aggr'); - if (foreignKey_1) { - // 基于属性的一对多 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push(function (result) { - var aggrResults = result.map(function (row) { - var _a, _b; - var aggrResult = aggregateFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([(_a = {}, - _a[foreignKey_1] = row.id, - _a), subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then(function (aggrResultResult) { - var _a; - return Object.assign(row, (_a = {}, - _a[attr] = aggrResultResult, - _a)); - }); - } - else { - Object.assign(row, (_b = {}, - _b[attr] = aggrResult, - _b)); + if (context instanceof AsyncRowStore_1.AsyncContext) { + // cascade的聚合运算只有后台需要执行 + if (foreignKey_1) { + // 基于属性的一对多 + if (isAggr) { + // 是聚合运算 + cascadeSelectionFns.push(function (result) { + var aggrResults = result.map(function (row) { + var _a, _b; + var aggrResult = aggregateFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([(_a = {}, + _a[foreignKey_1] = row.id, + _a), subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then(function (aggrResultResult) { + var _a; + return Object.assign(row, (_a = {}, + _a[attr] = aggrResultResult, + _a)); + }); + } + else { + Object.assign(row, (_b = {}, + _b[attr] = aggrResult, + _b)); + } + }); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then(function () { return undefined; }); } }); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then(function () { return undefined; }); - } - }); - } - else { - // 是一对多查询 - cascadeSelectionFns.push(function (result) { - var _a; - var ids = result.map(function (ele) { return ele.id; }); - var dealWithSubRows = function (subRows) { + } + else { + // 是一对多查询 + cascadeSelectionFns.push(function (result) { 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; }); - (0, assert_1.default)(subRowss); - Object.assign(ele, (_a = {}, - _a[attr] = subRowss, + 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)); - }); - } - }; - if (ids.length > 0) { - var subRows = cascadeSelectFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([(_a = {}, - _a[foreignKey_1] = { - $in: ids, - }, - _a), subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (subRows instanceof Promise) { - return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); - } - dealWithSubRows(subRows); - } - }); - } - } - else { - // 基于entity的多对一 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push(function (result) { - var aggrResults = result.map(function (row) { - var _a; - var aggrResult = aggregateFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([{ - entity: entity, - entityId: row.id, - }, subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then(function (aggrResultResult) { - var _a; - return Object.assign(row, (_a = {}, - _a[attr] = aggrResultResult, - _a)); - }); - } - else { - Object.assign(row, (_a = {}, - _a[attr] = aggrResult, - _a)); + } + else { + result.forEach(function (ele) { + var _a; + var subRowss = subRows.filter(function (ele2) { return ele2[foreignKey_1] === ele.id; }); + (0, assert_1.default)(subRowss); + Object.assign(ele, (_a = {}, + _a[attr] = subRowss, + _a)); + }); + } + }; + if (ids.length > 0) { + var subRows = cascadeSelectFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([(_a = {}, + _a[foreignKey_1] = { + $in: ids, + }, + _a), subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (subRows instanceof Promise) { + return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); + } + dealWithSubRows(subRows); } }); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then(function () { return undefined; }); - } - }); + } } else { - // 是一对多查询 - 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; }); - (0, assert_1.default)(subRowss); - Object.assign(ele, (_a = {}, - _a[attr] = subRowss, + // 基于entity的多对一 + if (isAggr) { + // 是聚合运算 + cascadeSelectionFns.push(function (result) { + var aggrResults = result.map(function (row) { + var _a; + var aggrResult = aggregateFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([{ + entity: entity, + entityId: row.id, + }, subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then(function (aggrResultResult) { + var _a; + return Object.assign(row, (_a = {}, + _a[attr] = aggrResultResult, + _a)); + }); + } + else { + Object.assign(row, (_a = {}, + _a[attr] = aggrResult, _a)); - }); + } + }); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then(function () { return undefined; }); } - }; - if (ids.length > 0) { - var subRows = cascadeSelectFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([{ - entity: entity, - entityId: { - $in: ids, - } - }, subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (subRows instanceof Promise) { - return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); + }); + } + else { + // 是一对多查询 + 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; }); + (0, assert_1.default)(subRowss); + Object.assign(ele, (_a = {}, + _a[attr] = subRowss, + _a)); + }); + } + }; + if (ids.length > 0) { + var subRows = cascadeSelectFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([{ + entity: entity, + entityId: { + $in: ids, + } + }, subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (subRows instanceof Promise) { + return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); + } + dealWithSubRows(subRows); } - dealWithSubRows(subRows); - } - }); + }); + } } } } diff --git a/src/store/CascadeStore.ts b/src/store/CascadeStore.ts index 71f2519..341b045 100644 --- a/src/store/CascadeStore.ts +++ b/src/store/CascadeStore.ts @@ -564,191 +564,194 @@ export abstract class CascadeStore exten const { data: subProjection, filter: subFilter, indexFrom, count, sorter: subSorter } = projection2[attr]; const [entity2, foreignKey] = relation; const isAggr = attr.endsWith('$$aggr'); - if (foreignKey) { - // 基于属性的一对多 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push( - (result) => { - const aggrResults = result.map( - (row) => { - const aggrResult = aggregateFn.call(this, entity2, { + if (context instanceof AsyncContext) { + // cascade的聚合运算只有后台需要执行 + if (foreignKey) { + // 基于属性的一对多 + if (isAggr) { + // 是聚合运算 + cascadeSelectionFns.push( + (result) => { + const aggrResults = result.map( + (row) => { + const aggrResult = aggregateFn.call(this, entity2, { + data: subProjection, + filter: combineFilters([{ + [foreignKey]: row.id, + }, subFilter]), + sorter: subSorter, + indexFrom, + count + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then( + (aggrResultResult) => Object.assign(row, { + [attr]: aggrResultResult, + }) + ); + } + else { + Object.assign(row, { + [attr]: aggrResult, + }); + } + } + ); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then( + () => undefined + ); + } + } + ); + } + else { + // 是一对多查询 + cascadeSelectionFns.push( + (result) => { + const ids = result.map( + ele => ele.id + ) as string[]; + + const dealWithSubRows = (subRows: Partial[]) => { + // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 + // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 + if (result.length == 1) { + Object.assign(result[0], { + [attr]: subRows, + }); + } + else { + result.forEach( + (ele) => { + const subRowss = subRows.filter( + ele2 => ele2[foreignKey] === ele.id + ); + assert(subRowss); + Object.assign(ele, { + [attr]: subRowss, + }); + } + ); + } + }; + + if (ids.length > 0) { + const subRows = cascadeSelectFn.call(this, entity2, { data: subProjection, filter: combineFilters([{ - [foreignKey]: row.id, + [foreignKey]: { + $in: ids, + } }, subFilter]), sorter: subSorter, indexFrom, count }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then( - (aggrResultResult) => Object.assign(row, { - [attr]: aggrResultResult, - }) + if (subRows instanceof Promise) { + return subRows.then( + (subRowss) => dealWithSubRows(subRowss) ); } - else { - Object.assign(row, { - [attr]: aggrResult, - }); - } + dealWithSubRows(subRows as any); } - ); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then( - () => undefined - ); } - } - ); + ); + } } else { - // 是一对多查询 - cascadeSelectionFns.push( - (result) => { - const ids = result.map( - ele => ele.id - ) as string[]; - - const dealWithSubRows = (subRows: Partial[]) => { - // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 - // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 - if (result.length == 1) { - Object.assign(result[0], { - [attr]: subRows, - }); - } - else { - result.forEach( - (ele) => { - const subRowss = subRows.filter( - ele2 => ele2[foreignKey] === ele.id + // 基于entity的多对一 + if (isAggr) { + // 是聚合运算 + cascadeSelectionFns.push( + (result) => { + const aggrResults = result.map( + (row) => { + const aggrResult = aggregateFn.call(this, entity2, { + data: subProjection, + filter: combineFilters([{ + entity, + entityId: row.id, + }, subFilter]), + sorter: subSorter, + indexFrom, + count + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then( + (aggrResultResult) => Object.assign(row, { + [attr]: aggrResultResult, + }) ); - assert(subRowss); - Object.assign(ele, { - [attr]: subRowss, + } + else { + Object.assign(row, { + [attr]: aggrResult, }); } + } + ); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then( + () => undefined ); } - }; - - if (ids.length > 0) { - const subRows = cascadeSelectFn.call(this, entity2, { - data: subProjection, - filter: combineFilters([{ - [foreignKey]: { - $in: ids, - } - }, subFilter]), - sorter: subSorter, - indexFrom, - count - }, context, option); - if (subRows instanceof Promise) { - return subRows.then( - (subRowss) => dealWithSubRows(subRowss) - ); - } - dealWithSubRows(subRows as any); } - } - ); - } - } - else { - // 基于entity的多对一 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push( - (result) => { - const aggrResults = result.map( - (row) => { - const aggrResult = aggregateFn.call(this, entity2, { + ); + } + else { + // 是一对多查询 + cascadeSelectionFns.push( + (result) => { + const ids = result.map( + ele => ele.id + ) as string[]; + const dealWithSubRows = (subRows: Partial[]) => { + // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 + // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 + if (result.length === 1) { + Object.assign(result[0], { + [attr]: subRows, + }); + } + else { + result.forEach( + (ele) => { + const subRowss = subRows.filter( + ele2 => ele2.entityId === ele.id + ); + assert(subRowss); + Object.assign(ele, { + [attr]: subRowss, + }); + } + ); + } + }; + + if (ids.length > 0) { + const subRows = cascadeSelectFn.call(this, entity2, { data: subProjection, filter: combineFilters([{ entity, - entityId: row.id, + entityId: { + $in: ids, + } }, subFilter]), sorter: subSorter, indexFrom, count }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then( - (aggrResultResult) => Object.assign(row, { - [attr]: aggrResultResult, - }) + if (subRows instanceof Promise) { + return subRows.then( + (subRowss) => dealWithSubRows(subRowss) ); } - else { - Object.assign(row, { - [attr]: aggrResult, - }); - } + dealWithSubRows(subRows as any); } - ); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then( - () => undefined - ); } - } - ); - } - else { - // 是一对多查询 - cascadeSelectionFns.push( - (result) => { - const ids = result.map( - ele => ele.id - ) as string[]; - const dealWithSubRows = (subRows: Partial[]) => { - // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 - // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 - if (result.length === 1) { - Object.assign(result[0], { - [attr]: subRows, - }); - } - else { - result.forEach( - (ele) => { - const subRowss = subRows.filter( - ele2 => ele2.entityId === ele.id - ); - assert(subRowss); - Object.assign(ele, { - [attr]: subRowss, - }); - } - ); - } - }; - - if (ids.length > 0) { - const subRows = cascadeSelectFn.call(this, entity2, { - data: subProjection, - filter: combineFilters([{ - entity, - entityId: { - $in: ids, - } - }, subFilter]), - sorter: subSorter, - indexFrom, - count - }, context, option); - if (subRows instanceof Promise) { - return subRows.then( - (subRowss) => dealWithSubRows(subRowss) - ); - } - dealWithSubRows(subRows as any); - } - } - ); + ); + } } } } From ccce584a1712691b577988e49939a72c5a88c718 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Sat, 6 May 2023 14:55:20 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E7=AC=94=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/CascadeStore.js | 303 +++++++++++++++++------------------ src/store/CascadeStore.ts | 327 +++++++++++++++++++------------------- 2 files changed, 312 insertions(+), 318 deletions(-) diff --git a/lib/store/CascadeStore.js b/lib/store/CascadeStore.js index c03b206..965bcf9 100644 --- a/lib/store/CascadeStore.js +++ b/lib/store/CascadeStore.js @@ -458,168 +458,165 @@ var CascadeStore = /** @class */ (function (_super) { var _g = projection2[attr], subProjection_1 = _g.data, subFilter_1 = _g.filter, indexFrom_1 = _g.indexFrom, count_1 = _g.count, subSorter_1 = _g.sorter; var _h = tslib_1.__read(relation, 2), entity2_1 = _h[0], foreignKey_1 = _h[1]; var isAggr = attr.endsWith('$$aggr'); - if (context instanceof AsyncRowStore_1.AsyncContext) { - // cascade的聚合运算只有后台需要执行 - if (foreignKey_1) { - // 基于属性的一对多 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push(function (result) { - var aggrResults = result.map(function (row) { - var _a, _b; - var aggrResult = aggregateFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([(_a = {}, - _a[foreignKey_1] = row.id, - _a), subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then(function (aggrResultResult) { - var _a; - return Object.assign(row, (_a = {}, - _a[attr] = aggrResultResult, - _a)); - }); - } - else { - Object.assign(row, (_b = {}, - _b[attr] = aggrResult, - _b)); - } - }); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then(function () { return undefined; }); - } - }); - } - else { - // 是一对多查询 - cascadeSelectionFns.push(function (result) { - 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, + if (foreignKey_1) { + // 基于属性的一对多 + if (isAggr) { + // 是聚合运算,只有后台才需要执行 + (context instanceof AsyncRowStore_1.AsyncContext) && cascadeSelectionFns.push(function (result) { + var aggrResults = result.map(function (row) { + var _a, _b; + var aggrResult = aggregateFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([(_a = {}, + _a[foreignKey_1] = row.id, + _a), subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then(function (aggrResultResult) { + var _a; + return Object.assign(row, (_a = {}, + _a[attr] = aggrResultResult, _a)); - } - else { - result.forEach(function (ele) { - var _a; - var subRowss = subRows.filter(function (ele2) { return ele2[foreignKey_1] === ele.id; }); - (0, assert_1.default)(subRowss); - Object.assign(ele, (_a = {}, - _a[attr] = subRowss, - _a)); - }); - } - }; - if (ids.length > 0) { - var subRows = cascadeSelectFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([(_a = {}, - _a[foreignKey_1] = { - $in: ids, - }, - _a), subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (subRows instanceof Promise) { - return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); - } - dealWithSubRows(subRows); + }); + } + else { + Object.assign(row, (_b = {}, + _b[attr] = aggrResult, + _b)); } }); - } + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then(function () { return undefined; }); + } + }); } else { - // 基于entity的多对一 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push(function (result) { - var aggrResults = result.map(function (row) { - var _a; - var aggrResult = aggregateFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([{ - entity: entity, - entityId: row.id, - }, subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then(function (aggrResultResult) { - var _a; - return Object.assign(row, (_a = {}, - _a[attr] = aggrResultResult, - _a)); - }); - } - else { - Object.assign(row, (_a = {}, - _a[attr] = aggrResult, + // 是一对多查询 + cascadeSelectionFns.push(function (result) { + 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; }); + (0, assert_1.default)(subRowss); + Object.assign(ele, (_a = {}, + _a[attr] = subRowss, _a)); - } - }); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then(function () { return undefined; }); + }); + } + }; + if (ids.length > 0) { + var subRows = cascadeSelectFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([(_a = {}, + _a[foreignKey_1] = { + $in: ids, + }, + _a), subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (subRows instanceof Promise) { + return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); + } + dealWithSubRows(subRows); + } + }); + } + } + else { + // 基于entity的多对一 + if (isAggr) { + // 是聚合运算,只有后台才需要执行 + (context instanceof AsyncRowStore_1.AsyncContext) && cascadeSelectionFns.push(function (result) { + var aggrResults = result.map(function (row) { + var _a; + var aggrResult = aggregateFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([{ + entity: entity, + entityId: row.id, + }, subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (aggrResult instanceof Promise) { + return aggrResult.then(function (aggrResultResult) { + var _a; + return Object.assign(row, (_a = {}, + _a[attr] = aggrResultResult, + _a)); + }); + } + else { + Object.assign(row, (_a = {}, + _a[attr] = aggrResult, + _a)); } }); - } - else { - // 是一对多查询 - 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; }); - (0, assert_1.default)(subRowss); - Object.assign(ele, (_a = {}, - _a[attr] = subRowss, - _a)); - }); - } - }; - if (ids.length > 0) { - var subRows = cascadeSelectFn.call(_this, entity2_1, { - data: subProjection_1, - filter: (0, filter_1.combineFilters)([{ - entity: entity, - entityId: { - $in: ids, - } - }, subFilter_1]), - sorter: subSorter_1, - indexFrom: indexFrom_1, - count: count_1 - }, context, option); - if (subRows instanceof Promise) { - return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); - } - dealWithSubRows(subRows); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then(function () { return undefined; }); + } + }); + } + else { + // 是一对多查询 + 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; }); + (0, assert_1.default)(subRowss); + Object.assign(ele, (_a = {}, + _a[attr] = subRowss, + _a)); + }); + } + }; + if (ids.length > 0) { + var subRows = cascadeSelectFn.call(_this, entity2_1, { + data: subProjection_1, + filter: (0, filter_1.combineFilters)([{ + entity: entity, + entityId: { + $in: ids, + } + }, subFilter_1]), + sorter: subSorter_1, + indexFrom: indexFrom_1, + count: count_1 + }, context, option); + if (subRows instanceof Promise) { + return subRows.then(function (subRowss) { return dealWithSubRows(subRowss); }); + } + dealWithSubRows(subRows); + } + }); } } } diff --git a/src/store/CascadeStore.ts b/src/store/CascadeStore.ts index 341b045..3c7e3b7 100644 --- a/src/store/CascadeStore.ts +++ b/src/store/CascadeStore.ts @@ -564,194 +564,191 @@ export abstract class CascadeStore exten const { data: subProjection, filter: subFilter, indexFrom, count, sorter: subSorter } = projection2[attr]; const [entity2, foreignKey] = relation; const isAggr = attr.endsWith('$$aggr'); - if (context instanceof AsyncContext) { - // cascade的聚合运算只有后台需要执行 - if (foreignKey) { - // 基于属性的一对多 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push( - (result) => { - const aggrResults = result.map( - (row) => { - const aggrResult = aggregateFn.call(this, entity2, { - data: subProjection, - filter: combineFilters([{ - [foreignKey]: row.id, - }, subFilter]), - sorter: subSorter, - indexFrom, - count - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then( - (aggrResultResult) => Object.assign(row, { - [attr]: aggrResultResult, - }) - ); - } - else { - Object.assign(row, { - [attr]: aggrResult, - }); - } - } - ); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then( - () => undefined - ); - } - } - ); - } - else { - // 是一对多查询 - cascadeSelectionFns.push( - (result) => { - const ids = result.map( - ele => ele.id - ) as string[]; - - const dealWithSubRows = (subRows: Partial[]) => { - // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 - // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 - if (result.length == 1) { - Object.assign(result[0], { - [attr]: subRows, - }); - } - else { - result.forEach( - (ele) => { - const subRowss = subRows.filter( - ele2 => ele2[foreignKey] === ele.id - ); - assert(subRowss); - Object.assign(ele, { - [attr]: subRowss, - }); - } - ); - } - }; - - if (ids.length > 0) { - const subRows = cascadeSelectFn.call(this, entity2, { + if (foreignKey) { + // 基于属性的一对多 + if (isAggr) { + // 是聚合运算,只有后台才需要执行 + (context instanceof AsyncContext) && cascadeSelectionFns.push( + (result) => { + const aggrResults = result.map( + (row) => { + const aggrResult = aggregateFn.call(this, entity2, { data: subProjection, filter: combineFilters([{ - [foreignKey]: { - $in: ids, - } + [foreignKey]: row.id, }, subFilter]), sorter: subSorter, indexFrom, count }, context, option); - if (subRows instanceof Promise) { - return subRows.then( - (subRowss) => dealWithSubRows(subRowss) + if (aggrResult instanceof Promise) { + return aggrResult.then( + (aggrResultResult) => Object.assign(row, { + [attr]: aggrResultResult, + }) ); } - dealWithSubRows(subRows as any); - } - } - ); - } - } - else { - // 基于entity的多对一 - if (isAggr) { - // 是聚合运算 - cascadeSelectionFns.push( - (result) => { - const aggrResults = result.map( - (row) => { - const aggrResult = aggregateFn.call(this, entity2, { - data: subProjection, - filter: combineFilters([{ - entity, - entityId: row.id, - }, subFilter]), - sorter: subSorter, - indexFrom, - count - }, context, option); - if (aggrResult instanceof Promise) { - return aggrResult.then( - (aggrResultResult) => Object.assign(row, { - [attr]: aggrResultResult, - }) - ); - } - else { - Object.assign(row, { - [attr]: aggrResult, - }); - } - } - ); - if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { - return Promise.all(aggrResults).then( - () => undefined - ); - } - } - ); - } - else { - // 是一对多查询 - cascadeSelectionFns.push( - (result) => { - const ids = result.map( - ele => ele.id - ) as string[]; - const dealWithSubRows = (subRows: Partial[]) => { - // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 - // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 - if (result.length === 1) { - Object.assign(result[0], { - [attr]: subRows, - }); - } else { - result.forEach( - (ele) => { - const subRowss = subRows.filter( - ele2 => ele2.entityId === ele.id - ); - assert(subRowss); - Object.assign(ele, { - [attr]: subRowss, - }); - } - ); + Object.assign(row, { + [attr]: aggrResult, + }); } - }; - - if (ids.length > 0) { - const subRows = cascadeSelectFn.call(this, entity2, { + } + ); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then( + () => undefined + ); + } + } + ); + } + else { + // 是一对多查询 + cascadeSelectionFns.push( + (result) => { + const ids = result.map( + ele => ele.id + ) as string[]; + + const dealWithSubRows = (subRows: Partial[]) => { + // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 + // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 + if (result.length == 1) { + Object.assign(result[0], { + [attr]: subRows, + }); + } + else { + result.forEach( + (ele) => { + const subRowss = subRows.filter( + ele2 => ele2[foreignKey] === ele.id + ); + assert(subRowss); + Object.assign(ele, { + [attr]: subRowss, + }); + } + ); + } + }; + + if (ids.length > 0) { + const subRows = cascadeSelectFn.call(this, entity2, { + data: subProjection, + filter: combineFilters([{ + [foreignKey]: { + $in: ids, + } + }, subFilter]), + sorter: subSorter, + indexFrom, + count + }, context, option); + if (subRows instanceof Promise) { + return subRows.then( + (subRowss) => dealWithSubRows(subRowss) + ); + } + dealWithSubRows(subRows as any); + } + } + ); + } + } + else { + // 基于entity的多对一 + if (isAggr) { + // 是聚合运算,只有后台才需要执行 + (context instanceof AsyncContext) && cascadeSelectionFns.push( + (result) => { + const aggrResults = result.map( + (row) => { + const aggrResult = aggregateFn.call(this, entity2, { data: subProjection, filter: combineFilters([{ entity, - entityId: { - $in: ids, - } + entityId: row.id, }, subFilter]), sorter: subSorter, indexFrom, count }, context, option); - if (subRows instanceof Promise) { - return subRows.then( - (subRowss) => dealWithSubRows(subRowss) + if (aggrResult instanceof Promise) { + return aggrResult.then( + (aggrResultResult) => Object.assign(row, { + [attr]: aggrResultResult, + }) ); } - dealWithSubRows(subRows as any); + else { + Object.assign(row, { + [attr]: aggrResult, + }); + } } + ); + if (aggrResults.length > 0 && aggrResults[0] instanceof Promise) { + return Promise.all(aggrResults).then( + () => undefined + ); } - ); - } + } + ); + } + else { + // 是一对多查询 + cascadeSelectionFns.push( + (result) => { + const ids = result.map( + ele => ele.id + ) as string[]; + const dealWithSubRows = (subRows: Partial[]) => { + // 这里如果result只有一行,则把返回结果直接置上,不对比外键值 + // 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理 + if (result.length === 1) { + Object.assign(result[0], { + [attr]: subRows, + }); + } + else { + result.forEach( + (ele) => { + const subRowss = subRows.filter( + ele2 => ele2.entityId === ele.id + ); + assert(subRowss); + Object.assign(ele, { + [attr]: subRowss, + }); + } + ); + } + }; + + if (ids.length > 0) { + const subRows = cascadeSelectFn.call(this, entity2, { + data: subProjection, + filter: combineFilters([{ + entity, + entityId: { + $in: ids, + } + }, subFilter]), + sorter: subSorter, + indexFrom, + count + }, context, option); + if (subRows instanceof Promise) { + return subRows.then( + (subRowss) => dealWithSubRows(subRowss) + ); + } + dealWithSubRows(subRows as any); + } + } + ); } } } From c9e8825af1357c7572560dc299db7c1239cfdb07 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Sat, 6 May 2023 17:17:23 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AAOtmKey=E7=9A=84=E5=AE=9A=E4=B9=89,=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E6=B2=A1=E7=94=A8=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/types/Entity.d.ts | 1 + src/types/Entity.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/lib/types/Entity.d.ts b/lib/types/Entity.d.ts index bb3642f..31a972f 100644 --- a/lib/types/Entity.d.ts +++ b/lib/types/Entity.d.ts @@ -181,4 +181,5 @@ export declare type Configuration = { actionType?: ActionType; static?: boolean; }; +export declare type OtmKey = K | `${K}$${number}`; export {}; diff --git a/src/types/Entity.ts b/src/types/Entity.ts index 9dafc79..0693faa 100644 --- a/src/types/Entity.ts +++ b/src/types/Entity.ts @@ -257,3 +257,6 @@ export type Configuration = { actionType?: ActionType; static?: boolean; // 标识是维表(变动较小,相对独立) }; + +// 一对多的键值的扩展 +export type OtmKey = K | `${K}$${number}`; \ No newline at end of file From 3fb8a49be0d5ce60899e18d4e628c74a34b417cc Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Sat, 6 May 2023 17:17:45 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E6=9A=82=E5=AD=98=E7=9A=84schemaBuilder?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/schemaBuilder.ts.temp | 6362 ++++++++++++++++++++++++++++ 1 file changed, 6362 insertions(+) create mode 100644 src/compiler/schemaBuilder.ts.temp diff --git a/src/compiler/schemaBuilder.ts.temp b/src/compiler/schemaBuilder.ts.temp new file mode 100644 index 0000000..c268139 --- /dev/null +++ b/src/compiler/schemaBuilder.ts.temp @@ -0,0 +1,6362 @@ +// 这个版本搞定了一对多的键值支持多个,但在4.7.4版本ts下貌似有bug。目前ts还不能升级,升级在expression那里的递归解释不了,等以后再处理 +/** 代码暂存,主要是在Schema, Projection, CreateData, UpdateData四个类型上,增加了类似 +{ + [A in OtmKey<"modiEntity$entity">]?: ModiEntity.Selection & { + $entity: "modiEntity"; + }; +} +这样的声明结构 +**/ + +import PathLib from 'path'; +import assert from 'assert'; +import { writeFileSync, readdirSync, mkdirSync, fstat } from 'fs'; +import { emptydirSync } from 'fs-extra'; +import { assign, cloneDeep, difference, identity, intersection, keys, pull, uniq, uniqBy } from 'lodash'; +import * as ts from 'typescript'; +const { factory } = ts; +import { + ACTION_CONSTANT_IN_OAK_DOMAIN, + TYPE_PATH_IN_OAK_DOMAIN, + RESERVED_ENTITIES, + STRING_LITERAL_MAX_LENGTH, + NUMERICAL_LITERL_DEFAULT_PRECISION, + NUMERICAL_LITERL_DEFAULT_SCALE, + INT_LITERL_DEFAULT_WIDTH, + LIB_OAK_DOMAIN, + ENTITY_NAME_MAX_LENGTH, +} from './env'; +import { firstLetterLowerCase, firstLetterUpperCase } from '../utils/string'; + +const Schema: Record; + fulltextIndex?: true; + indexes?: ts.ArrayLiteralExpression; + sourceFile: ts.SourceFile; + enumAttributes: Record; + locale: ts.ObjectLiteralExpression; + // relationHierarchy?: ts.ObjectLiteralExpression; + // reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression; + toModi: boolean; + actionType: string; + static: boolean; + inModi: boolean; + hasRelationDef: false | ts.TypeAliasDeclaration; + additionalImports: ts.ImportDeclaration[], +}> = {}; +const OneToMany: Record> = {}; +const ManyToOne: Record> = {}; +const ReversePointerEntities: Record = {}; +const ReversePointerRelations: Record = {}; + +const ActionImportStatements = () => [ + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ActionDef") + )]) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}Action`), + undefined + ), + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("GenericAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("AppendOnlyAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ReadOnlyAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ExcludeUpdateAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ExcludeRemoveAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("RelationAction") + ), + ]) + ), + factory.createStringLiteral(ACTION_CONSTANT_IN_OAK_DOMAIN()), + undefined + ) +]; + +const ActionAsts: { + [module: string]: { + statements: Array; + sourceFile: ts.SourceFile; + importedFrom: Record; + // actionNames: string[]; + actionDefNames: string[]; + }; +} = {}; + +const SchemaAsts: { + [module: string]: { + statements: Array; + sourceFile: ts.SourceFile; + }; +} = {}; + +function addRelationship(many: string, one: string, key: string, notNull: boolean) { + const { [many]: manySet } = ManyToOne; + const one2 = one === 'Schema' ? many : one; + if (manySet) { + manySet.push([one2, key, notNull]); + } + else { + assign(ManyToOne, { + [many]: [[one2, key, notNull]], + }); + } + + const { [one2]: oneSet } = OneToMany; + if (oneSet) { + oneSet.push([many, key, notNull]); + } + else { + assign(OneToMany, { + [one2]: [[many, key, notNull]], + }); + } +} + +/** + * 对relationship去重。一旦发生对象重定义这里就有可能重复 + */ +function uniqRelationships() { + for (const entity in ManyToOne) { + ManyToOne[entity] = uniqBy(ManyToOne[entity], (ele) => `${ele[0]}-${ele[1]}`); + } + for (const entity in OneToMany) { + OneToMany[entity] = uniqBy(OneToMany[entity], (ele) => `${ele[0]}-${ele[1]}`); + } + for (const entity in ReversePointerRelations) { + OneToMany[entity] = uniq(OneToMany[entity]); + } +} + + +function createForeignRef(entity: string, foreignKey: string, ref: string) { + if (entity === foreignKey) { + return factory.createIdentifier(ref) + } + return factory.createQualifiedName( + factory.createIdentifier(foreignKey), + factory.createIdentifier(ref) + ); +} + +function pushStatementIntoActionAst( + moduleName: string, + node: ts.Statement, + sourceFile: ts.SourceFile) { + + // let actionNames; + let actionDefName; + /* if (ts.isTypeAliasDeclaration(node) && node.name.text === 'ParticularAction') { + const { type } = node; + if (ts.isUnionTypeNode(type)) { + actionNames = type.types.map( + (ele) => { + assert(ts.isTypeReferenceNode(ele)); + const text = (ele.typeName).text; + assert(text.endsWith('Action')); + return firstLetterLowerCase(text.slice(0, text.length - 6)); + } + ) + } + else { + assert(ts.isTypeReferenceNode(type)); + const text = (type.typeName).text; + assert(text.endsWith('Action')); + actionNames = [firstLetterLowerCase(text.slice(0, text.length - 6))]; + } + } */ + + if (ts.isVariableStatement(node)) { + const { declarationList: { declarations } } = node; + declarations.forEach( + (declaration) => { + if (ts.isIdentifier(declaration.name) && declaration.name.text.endsWith('ActionDef')) { + const { text } = declaration.name; + actionDefName = firstLetterLowerCase(text.slice(0, text.length - 9)); + } + } + ); + } + + if (ActionAsts[moduleName]) { + ActionAsts[moduleName].statements.push(node); + /* if (actionNames) { + ActionAsts[moduleName].actionNames = actionNames; + } */ + if (actionDefName) { + ActionAsts[moduleName].actionDefNames.push(actionDefName); + } + } + else { + assign(ActionAsts, { + [moduleName]: { + statements: [...ActionImportStatements(), node], + sourceFile, + importedFrom: {}, + // actionNames, + actionDefNames: actionDefName ? [actionDefName] : [], + } + }); + } +} + +function pushStatementIntoSchemaAst(moduleName: string, statement: ts.Statement, sourceFile: ts.SourceFile) { + if (SchemaAsts[moduleName]) { + SchemaAsts[moduleName].statements.push(statement); + } + else { + assign(SchemaAsts, { + [moduleName]: { + statements: [statement], + sourceFile, + } + }); + } +} + +/** + * 检查ActionDef是否满足合法的定义 + * 1、ActionDef, Action, State三者命名是否一致 + * @param actionDefNode + */ +function checkActionDefNameConsistent(filename: string, actionDefNode: ts.VariableDeclaration) { + const { name, type } = actionDefNode; + assert(ts.isTypeReferenceNode(type!)); + const { typeArguments } = type!; + assert(typeArguments!.length === 2); + const [actionNode, stateNode] = typeArguments!; + + assert(ts.isIdentifier(name), `文件${filename}中的ActionDef${(name).text}不是一个有效的变量`); + assert(name.text.endsWith('ActionDef'), `文件${filename}中的ActionDef${name.text}未以ActionDef结尾`); + assert(ts.isTypeReferenceNode(actionNode) && ts.isTypeReferenceNode(stateNode), `文件${filename}中的ActionDef${name.text}类型声明中的action和state非法`); + assert(ts.isIdentifier(actionNode.typeName) && ts.isIdentifier(stateNode.typeName)); + assert(actionNode.typeName.text.endsWith('Action'), `文件${filename}中的ActionDef${name.text}所引用的Action${actionNode.typeName}未以Action结尾`); + assert(stateNode.typeName.text.endsWith('State'), `文件${filename}中的ActionDef${name.text}所引用的Action${stateNode.typeName}未以Action结尾`); + const adfName = name.text.slice(0, name.text.length - 9); + const aName = actionNode.typeName.text.slice(0, actionNode.typeName.text.length - 6); + const sName = stateNode.typeName.text.slice(0, stateNode.typeName.text.length - 5); + + assert(adfName === aName && aName === sName, `文件${filename}中的ActionDef${name.text}中ActionDef, Action和State的命名规则不一致`); +} + + +function checkStringLiteralLegal(filename: string, obj: string, text: string, ele: ts.TypeNode) { + assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `${filename}中引用的${obj} ${text}中存在不是stringliteral的类型`); + assert(!ele.literal.text.includes('$'), `${filename}中引用的action${text}中的${obj}「${ele.literal.text}」包含非法字符$`); + assert(ele.literal.text.length > 0, `${filename}中引用的action${text}中的${obj}「${ele.literal.text}」长度非法`); + assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `${filename}中引用的${obj} ${text}中的「${ele.literal.text}」长度过长`); + return ele.literal.text; +} + +function addActionSource(moduleName: string, name: ts.Identifier, node: ts.ImportDeclaration) { + const ast = ActionAsts[moduleName]; + const { moduleSpecifier } = node; + + // todo 目前应该只会引用oak-domain/src/actions/action里的公共action,未来如果有交叉引用这里代码要修正(如果domain中也有引用action_constants这里应该也会错) + assert(ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === ACTION_CONSTANT_IN_OAK_DOMAIN()); + assign(ast.importedFrom, { + [name.text]: node, + }); +} + +function getStringTextFromUnionStringLiterals(moduleName: string, filename: string, node: ts.TypeReferenceNode, program: ts.Program) { + const checker = program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node.typeName); + let declaration = symbol?.getDeclarations()![0]!; + let isImport = false; + /* const typee = checker.getDeclaredTypeOfSymbol(symbol!); + + const declaration = typee.aliasSymbol!.getDeclarations()![0]; */ + if (ts.isImportSpecifier(declaration)) { + isImport = true; + const typee = checker.getDeclaredTypeOfSymbol(symbol!); + declaration = typee.aliasSymbol!.getDeclarations()![0]; + } + + assert(ts.isTypeAliasDeclaration(declaration)); + const { type, name } = declaration; + // assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${filename}中引用的action「${(name).text}」的定义不是union和stringLiteral类型`); + + // 如果这个action是从外部导入的,在这里要记下来此entity和这个导入之间的关系 + if (isImport) { + const importDeclartion = symbol!.getDeclarations()![0]!.parent.parent.parent; + + assert(ts.isImportDeclaration(importDeclartion)); + addActionSource(moduleName, name, importDeclartion); + } + else { + const ast = ActionAsts[moduleName]; + assign(ast.importedFrom, { + [name.text]: 'local', + }); + } + + + if (ts.isUnionTypeNode(type)) { + const actions = type.types!.map( + ele => checkStringLiteralLegal(filename, 'action', (name).text, ele) + ); + + return actions; + } + else { + assert(ts.isLiteralTypeNode(type!), `${filename}中引用的action「${(name).text}」的定义不是union和stringLiteral类型`); + const action = checkStringLiteralLegal(filename, 'action', (name).text, type); + return [action]; + } +} + +const RESERVED_ACTION_NAMES = ['GenericAction', 'ParticularAction', 'ExcludeRemoveAction', 'ExcludeUpdateAction', 'ReadOnlyAction', 'AppendOnlyAction', 'RelationAction']; +import { genericActions, relationActions } from '../actions/action'; +import { unIndexedTypes } from '../types/DataType'; +import { initinctiveAttributes } from '../types/Entity'; + +const OriginActionDict = { + 'crud': 'GenericAction', + 'excludeUpdate': 'ExcludeUpdateAction', + 'excludeRemove': 'ExcludeRemoveAction', + 'appendOnly': 'AppendOnlyAction', + 'readOnly': 'ReadOnlyAction', +}; +function dealWithActions(moduleName: string, filename: string, node: ts.TypeNode, program: ts.Program, sourceFile: ts.SourceFile, hasRelationDef?: boolean) { + const actionTexts = genericActions.map( + ele => ele + ); + if (hasRelationDef) { + actionTexts.push(...relationActions); + } + if (ts.isUnionTypeNode(node)) { + const actionNames = node.types.map( + ele => { + if (ts.isTypeReferenceNode(ele) && ts.isIdentifier(ele.typeName)) { + return ele.typeName.text; + } + } + ).filter( + ele => !!ele + ); + assert(intersection(actionNames, RESERVED_ENTITIES).length === 0, + `${filename}中的Action命名不能是「${RESERVED_ACTION_NAMES.join(',')}」之一`); + + node.types.forEach( + ele => { + if (ts.isTypeReferenceNode(ele)) { + actionTexts.push(...getStringTextFromUnionStringLiterals(moduleName, filename, ele, program)); + } + else { + assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `【${moduleName}】action的定义既非Type也不是string`); + actionTexts.push(ele.literal.text); + } + } + ); + } + else if (ts.isTypeReferenceNode(node)) { + if (ts.isIdentifier(node.typeName)) { + assert(!RESERVED_ACTION_NAMES.includes(node.typeName.text), + `${filename}中的Action命名不能是「${RESERVED_ACTION_NAMES.join(',')}」之一`); + } + actionTexts.push(...getStringTextFromUnionStringLiterals(moduleName, filename, node, program)); + } + else { + assert(ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal), `【${moduleName}】action的定义既非Type也不是string`); + actionTexts.push(node.literal.text); + } + + // 所有的action定义不能有重名 + const ActionDict = {}; + actionTexts.forEach( + (action) => { + assert(action.length <= STRING_LITERAL_MAX_LENGTH, `${filename}中的Action「${action}」命名长度大于${STRING_LITERAL_MAX_LENGTH}`); + assert(/^[a-z][a-z|A-Z]+$/.test(action), `${filename}中的Action「${action}」命名不合法,必须以小字字母开头且只能包含字母`) + if (ActionDict.hasOwnProperty(action)) { + throw new Error(`文件${filename}中,Action定义上的【${action}】动作存在同名`); + } + else { + assign(ActionDict, { + [action]: 1, + }); + } + } + ); + + pushStatementIntoActionAst(moduleName, + factory.createVariableStatement( + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("actions"), + undefined, + undefined, + factory.createArrayLiteralExpression( + actionTexts.map( + ele => factory.createStringLiteral(ele) + ), + false + ) + )], + ts.NodeFlags.Const + ) + ), + sourceFile + ); +} + +/** + * entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式 + * @param declaration + * @param filename + * @returns + */ +function getEntityImported(declaration: ts.ImportDeclaration) { + const { moduleSpecifier, importClause } = declaration; + let entityImported: string | undefined; + + if (ts.isStringLiteral(moduleSpecifier)) { + const { name: importedFileName } = PathLib.parse(moduleSpecifier.text); + const { namedBindings } = importClause!; + if (namedBindings && ts.isNamedImports(namedBindings)) { + const { elements } = namedBindings; + if (elements.find( + ele => ts.isImportSpecifier(ele) && ele.name.text === importedFileName && ele.propertyName?.text === 'Schema' + )) { + entityImported = importedFileName; + } + } + } + return entityImported; +} + +function checkLocaleEnumAttrs(node: ts.TypeLiteralNode, attrs: string[], filename: string) { + const { members } = node; + const memberKeys = members.map( + (ele) => { + assert(ts.isPropertySignature(ele) && ts.isIdentifier(ele.name)); + return ele.name.text; + } + ); + + const lack = difference(attrs, memberKeys); + if (lack.length > 0) { + throw new Error(`${filename}中缺少了对${lack.join(',')}属性的locale定义`); + } +} + +function checkLocaleExpressionPropertyExists(root: ts.ObjectLiteralExpression, attr: string, exists: boolean, filename: string) { + const { properties } = root; + properties.forEach( + (ele) => { + assert(ts.isPropertyAssignment(ele) && (ts.isIdentifier(ele.name) || ts.isStringLiteral(ele.name)) && ts.isObjectLiteralExpression(ele.initializer)); + const { properties: p2 } = ele.initializer; + const pp = p2.find( + (ele2) => { + assert(ts.isPropertyAssignment(ele2) && ts.isIdentifier(ele2.name)); + return ele2.name.text === attr; + } + ); + if (exists && !pp) { + throw new Error(`${filename}中的locale定义中的${ele.name.text}中缺少了${attr}的定义`); + } + else if (!exists && pp) { + throw new Error(`${filename}中的locale定义中的${ele.name.text}中有多余的${attr}定义`); + } + } + ) +} + +function getStringEnumValues(filename: string, program: ts.Program, obj: string, node: ts.TypeReferenceNode) { + const checker = program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node.typeName); + let declaration = symbol?.getDeclarations()![0]!; + if (ts.isImportSpecifier(declaration)) { + const typee = checker.getDeclaredTypeOfSymbol(symbol!); + declaration = typee.aliasSymbol?.getDeclarations()![0]!; + } + + if (declaration && ts.isTypeAliasDeclaration(declaration)) { + if (ts.isUnionTypeNode(declaration.type) && ts.isLiteralTypeNode(declaration.type.types[0])) { + return declaration.type.types.map( + ele => checkStringLiteralLegal(filename, obj, (declaration).name.text, ele) + ) + } + if (ts.isLiteralTypeNode(declaration.type)) { + const value = checkStringLiteralLegal(filename, obj, declaration.name.text, declaration.type); + return [value]; + } + } +} + +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); + const moduleName = filename.split('.')[0]; + + if (Schema.hasOwnProperty(moduleName)) { + delete ActionAsts[moduleName]; + delete SchemaAsts[moduleName]; + // removeFromRelationShip(moduleName); + console.warn(`出现了同名的Entity定义「${moduleName}」,将使用${fullPath}取代掉默认对象,请检查新的对象结构及相关常量定义与原有的兼容,否则原有对象的相关逻辑会出现不可知异常`); + } + checkNameLegal(filename, moduleName, true); + + const referencedSchemas: string[] = []; + const schemaAttrs: ts.TypeElement[] = []; + let hasFulltextIndex: boolean = false; + let indexes: ts.ArrayLiteralExpression; + let beforeSchema = true; + let hasActionDef = false; + let hasRelationDef: boolean | ts.TypeAliasDeclaration = false; + let hasActionOrStateDef = false; + let toModi = false; + let actionType = 'crud'; + let _static = false; + const enumAttributes: Record = {}; + const additionalImports: ts.ImportDeclaration[] = []; + let localeDef: ts.ObjectLiteralExpression | undefined = undefined; + // let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined; + // let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined; + ts.forEachChild(sourceFile!, (node) => { + if (ts.isImportDeclaration(node)) { + const entityImported = getEntityImported(node); + if (entityImported) { + referencedSchemas.push(entityImported); + } + else if (!process.env.COMPLING_IN_DOMAIN && !relativePath?.startsWith(LIB_OAK_DOMAIN)) { + /**import了domain以外的其它定义类型,需要被复制到生成的Schema文件中 + * 这里必须注意,1、假设了domain当中定义的几个entity不会引用其它文件上的定义(除了type里的那些通用类型,默认都会被输出到文件中) + * 2、假设了其它项目文件不会引用domain当中除了type通用类型之外的其它内容,否则不会被输出到文件中 + * 这里主要是对import的处理比较粗略,日后有需要的时候再精修 + */ + const { moduleSpecifier, importClause } = node; + if (ts.isStringLiteral(moduleSpecifier) && !moduleSpecifier.text.startsWith(LIB_OAK_DOMAIN)) { + // 编译后的路径默认要深一层 + const moduleSpecifier2Text = relativePath + ? PathLib.join( + relativePath, + moduleSpecifier.text + ).replace(/\\/g, '/') + : PathLib.join('..', moduleSpecifier.text).replace( + /\\/g, + '/' + ); + additionalImports.push( + factory.updateImportDeclaration( + node, + undefined, + undefined, + importClause, + factory.createStringLiteral(moduleSpecifier2Text), + undefined + ) + ); + } + } + } + + if (ts.isInterfaceDeclaration(node)) { + // schema 定义 + if (node.name.text === 'Schema') { + assert(!localeDef, `【${filename}】locale定义须在Schema之后`); + let hasEntityAttr = false; + let hasEntityIdAttr = false; + const { members, heritageClauses } = node; + assert(['EntityShape'].includes((heritageClauses![0].types![0].expression).text), moduleName); + members.forEach( + (attrNode) => { + const { type, name, questionToken } = attrNode; + const 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')) { + addRelationship(moduleName, type.typeName.text, attrName, !!questionToken); + schemaAttrs.push(attrNode); + } + else if (type.typeName.text === 'Array') { + // 这是一对多的反向指针的引用,需要特殊处理 + const { typeArguments } = type; + assert(typeArguments!.length === 1 + && ts.isTypeReferenceNode(typeArguments![0]) + && ts.isIdentifier(typeArguments![0].typeName) + && referencedSchemas.includes(typeArguments![0].typeName.text), + `「${filename}」非法的属性定义「${attrName}」`); + const reverseEntity = typeArguments![0].typeName.text; + if (ReversePointerRelations[reverseEntity]) { + if (!ReversePointerRelations[reverseEntity].includes(moduleName)) { + ReversePointerRelations[reverseEntity].push(moduleName); + } + } + else { + assign(ReversePointerRelations, { + [reverseEntity]: [moduleName], + }); + } + + if (reverseEntity === 'Modi') { + toModi = true; + } + } + else { + schemaAttrs.push(attrNode); + const enumStringValues = getStringEnumValues(filename, program, '属性', type); + if (enumStringValues) { + enumAttributes[attrName] = enumStringValues; + } + } + } + else if (ts.isArrayTypeNode(type!) && ts.isTypeReferenceNode(type.elementType) && ts.isIdentifier(type.elementType.typeName)) { + const { typeName } = type.elementType; + + if (referencedSchemas.includes(typeName.text)) { + // 这也是一对多的反指定义 + const reverseEntity = typeName.text; + if (ReversePointerRelations[reverseEntity]) { + ReversePointerRelations[reverseEntity].push(moduleName); + } + else { + assign(ReversePointerRelations, { + [reverseEntity]: [moduleName], + }); + } + + if (reverseEntity === 'Modi') { + toModi = true; + } + } + else { + throw new Error(`对象${moduleName}中定义的属性${attrName}是不可识别的数组类别`); + } + } + else { + schemaAttrs.push(attrNode); + if (ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) && ts.isStringLiteral(type.types[0].literal)) { + assert(ts.isIdentifier(name)); + const { types } = type; + const enumValues = types.map( + (ele) => checkStringLiteralLegal(filename, '属性', name.text, ele) + ); + enumAttributes[name.text] = enumValues; + } + else if (ts.isLiteralTypeNode(type!) && ts.isStringLiteral(type.literal)) { + // 单个字符串的情形,目前应该没有,没测试过,先写着 by Xc 20230221 + assert(ts.isIdentifier(name)); + const enumValues = [ + checkStringLiteralLegal(filename, '属性', name.text, type) + ]; + enumAttributes[name.text] = enumValues; + } + } + + if (attrName === 'entity') { + assert(ts.isTypeReferenceNode(type!) && ts.isIdentifier(type.typeName), `「${moduleName}」中entity属性的定义不是String<32>类型,entity是系统用于表示反指指针的保留属性,请勿他用`); + const { typeArguments } = type; + assert(type.typeName.text === 'String' + && typeArguments + && typeArguments.length === 1, `「${moduleName}」中entity属性的定义不是String<32>类型,entity是系统用于表示反指指针的保留属性,请勿他用`); + const [node] = typeArguments; + if (ts.isLiteralTypeNode(node) && ts.isNumericLiteral(node.literal)) { + if (parseInt(node.literal.text) > 32) { + assert(false, `「${moduleName}」中entity属性的定义不是String<32>类型,entity是系统用于表示反指指针的保留属性,请勿他用`); + } + else { + hasEntityAttr = true; + } + } + } + + if (attrName === 'entityId') { + assert(ts.isTypeReferenceNode(type!) && ts.isIdentifier(type.typeName), `「${moduleName}」中entityId属性的定义不是String<64>类型,entityId是系统用于表示反指指针的保留属性,请勿他用`); + const { typeArguments } = type; + assert(type.typeName.text === 'String' && typeArguments && typeArguments.length === 1, `「${moduleName}」中entityId属性的定义不是String<64>类型,entityId是系统用于表示反指指针的保留属性,请勿他用`); + + const [node] = typeArguments; + if (ts.isLiteralTypeNode(node) && ts.isNumericLiteral(node.literal)) { + if (parseInt(node.literal.text) !== 64) { + assert(false, `「${moduleName}」中entityId属性的定义不是String<64>类型,entityId是系统用于表示反指指针的保留属性,请勿他用`); + } + else { + hasEntityIdAttr = true; + } + } + } + } + ); + if (hasEntityAttr && hasEntityIdAttr) { + assign(ReversePointerEntities, { + [moduleName]: 1, + }); + } + else if (hasEntityAttr || hasEntityIdAttr) { + throw new Error(`文件「${filename}」:属性 定义中只包含${hasEntityAttr ? 'entity' : 'entityId'},不符合定义规范。entity/entityId必须联合出现,代表不定对象的反向指针`); + } + beforeSchema = false; + + // 对于不是Modi和Oper的对象,全部建立和ModiEntity的反指关系 + if (!['Modi', 'Oper', 'OperEntity', 'ModiEntity'].includes(moduleName) && !toModi) { + if (ReversePointerRelations['ModiEntity']) { + if (!ReversePointerRelations['ModiEntity'].includes(moduleName)) { + ReversePointerRelations['ModiEntity'].push(moduleName); + } + } + else { + assign(ReversePointerRelations, { + ['ModiEntity']: [moduleName], + }); + } + + if (ReversePointerRelations['OperEntity']) { + if (!ReversePointerRelations['OperEntity'].includes(moduleName)) { + ReversePointerRelations['OperEntity'].push(moduleName); + } + } + else { + assign(ReversePointerRelations, { + ['OperEntity']: [moduleName], + }); + } + } + + } + else if (beforeSchema) { + // 本地规定的一些形状定义,直接使用 + pushStatementIntoSchemaAst(moduleName, node, sourceFile!); + } + } + + if (ts.isTypeAliasDeclaration(node)) { + // action 定义 + if (node.name.text === 'Action') { + assert(!localeDef, `【${filename}】locale定义须在Action之后`); + hasActionDef = true; + const modifiers = [factory.createModifier(ts.SyntaxKind.ExportKeyword)]; + pushStatementIntoActionAst( + moduleName, + factory.updateTypeAliasDeclaration( + node, + node.decorators, + modifiers, + factory.createIdentifier('ParticularAction'), + node.typeParameters, + node.type + ), + sourceFile! + ); + const actionDefNodes = [ + factory.createTypeReferenceNode( + OriginActionDict[actionType as keyof typeof OriginActionDict], + undefined + ), + factory.createTypeReferenceNode( + 'ParticularAction', + undefined + ) + ] as ts.TypeNode[]; + if (hasRelationDef || moduleName === 'User') { + actionDefNodes.push( + factory.createTypeReferenceNode( + 'RelationAction', + undefined + ) + ); + } + if (process.env.COMPLING_AS_LIB) { + actionDefNodes.push( + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ); + } + pushStatementIntoActionAst( + moduleName, + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Action"), + undefined, + factory.createUnionTypeNode(actionDefNodes) + ), + sourceFile! + ); + dealWithActions(moduleName, filename, node.type, program, sourceFile!, !!hasRelationDef || moduleName === 'User'); + } + else if (node.name.text === 'Relation') { + assert(!hasActionDef, `【${filename}】action定义须在Relation之后`); + assert(!localeDef, `【${filename}】locale定义须在Relation之后`); + // 增加userXXX对象的描述 + const relationValues = [] as string[]; + if (ts.isLiteralTypeNode(node.type)) { + assert(ts.isStringLiteral(node.type.literal)); + assert(node.type.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${node.type.literal.text})`); + relationValues.push(node.type.literal.text); + } + else { + assert(ts.isUnionTypeNode(node.type), `Relation的定义只能是string类型(${filename})`); + relationValues.push(...node.type.types.map( + (ele) => { + assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `Relation的定义只能是string类型(${filename})`); + assert(ele.literal.text.length < STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${STRING_LITERAL_MAX_LENGTH}(${filename},${ele.literal.text})`); + return ele.literal.text; + } + )); + } + const entityLc = firstLetterLowerCase(moduleName); + const relationEntityName = `User${moduleName}`; + const relationSchemaAttrs: ts.TypeElement[] = [ + factory.createPropertySignature( + undefined, + factory.createIdentifier("user"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("User"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(entityLc), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier(moduleName), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('relation'), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Relation"), + undefined + ) + ), + ]; + assign(Schema, { + [relationEntityName]: { + schemaAttrs: relationSchemaAttrs, + sourceFile, + enumAttributes: { + relation: relationValues, + }, + actionType: 'excludeUpdate', + additionalImports: [ + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("Relation") + )]) + ), + factory.createStringLiteral(`../${moduleName}/Schema`), + undefined + ) + ], + }, + }); + addRelationship(relationEntityName, 'User', 'user', true); + addRelationship(relationEntityName, moduleName, entityLc, true); + + // 对UserEntityGrant对象,建立相应的反指关系 + if (ReversePointerRelations['UserEntityGrant']) { + if (!ReversePointerRelations['UserEntityGrant'].includes(moduleName)) { + ReversePointerRelations['UserEntityGrant'].push(moduleName); + } + } + else { + assign(ReversePointerRelations, { + ['UserEntityGrant']: [moduleName], + }); + } + + hasRelationDef = node; + } + else if (node.name.text.endsWith('Action') || node.name.text.endsWith('State')) { + assert(!localeDef, `【${filename}】locale定义须在Action/State之后`); + hasActionOrStateDef = true; + const { type } = node; + if (ts.isUnionTypeNode(type)) { + pushStatementIntoActionAst(moduleName, + factory.updateTypeAliasDeclaration( + node, + node.decorators, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + node.name, + node.typeParameters, + process.env.COMPLING_AS_LIB ? factory.createUnionTypeNode([ + ...type.types, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ]) : type + ), + sourceFile!); + } + else { + assert(ts.isLiteralTypeNode(type) || ts.isTypeReferenceNode(type), `${moduleName} - ${node.name}`); + pushStatementIntoActionAst(moduleName, + factory.updateTypeAliasDeclaration( + node, + node.decorators, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + node.name, + node.typeParameters, + process.env.COMPLING_AS_LIB ? factory.createUnionTypeNode([ + type, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ]) : type + ), + sourceFile!); + } + } + else if (beforeSchema) { + // 本地规定的一些形状定义,直接使用 + pushStatementIntoSchemaAst(moduleName, node, sourceFile!); + } + } + + if (ts.isVariableStatement(node)) { + const { declarationList: { declarations } } = node; + declarations.forEach( + (declaration) => { + if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') { + checkActionDefNameConsistent(filename, declaration); + const { typeArguments } = declaration.type; + assert(typeArguments!.length === 2); + const [actionNode, stateNode] = typeArguments!; + + assert(ts.isTypeReferenceNode(actionNode)); + assert(ts.isTypeReferenceNode(stateNode)); + assert(getStringEnumValues(filename, program, 'action', (actionNode)), `文件${filename}中的action${(actionNode.typeName).text}定义不是字符串类型`); + const enumStateValues = getStringEnumValues(filename, program, 'state', stateNode); + assert(enumStateValues, `文件${filename}中的state${(stateNode.typeName).text}定义不是字符串类型`) + + pushStatementIntoActionAst(moduleName, node, sourceFile!); + + assert(ts.isIdentifier(declaration.name)); + const adName = declaration.name.text.slice(0, declaration.name.text.length - 9); + const attr = adName.concat('State'); + schemaAttrs.push( + factory.createPropertySignature( + undefined, + firstLetterLowerCase(attr), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + attr, + ) + ) + ); + enumAttributes[firstLetterLowerCase(attr)] = enumStateValues; + } + else if (declaration.type && (ts.isArrayTypeNode(declaration.type!) + && ts.isTypeReferenceNode(declaration.type.elementType) + && ts.isIdentifier(declaration.type.elementType.typeName) + && declaration.type.elementType.typeName.text === 'Index' + || ts.isTypeReferenceNode(declaration.type!) + && ts.isIdentifier(declaration.type.typeName) + && declaration.type.typeName.text === 'Array' + && ts.isTypeReferenceNode(declaration.type.typeArguments![0]) + && ts.isIdentifier(declaration.type.typeArguments![0].typeName) + && declaration.type.typeArguments![0].typeName.text === 'Index')) { + // 对索引Index的定义 + const indexNameDict: Record = {}; + assert(ts.isArrayLiteralExpression(declaration.initializer!), `「${filename}」Index「${declaration.name.getText()}」的定义必须符合规范`); + + // todo 这里应该先做一个类型检查的,但不知道怎么写 by Xc + // 检查索引的属性是否合法 + const { elements } = declaration.initializer; + elements.forEach( + (ele) => { + let isFulltextIndex = false; + assert(ts.isObjectLiteralExpression(ele)); + const { properties } = ele; + const attrProperty = properties.find( + (ele2) => { + assert(ts.isPropertyAssignment(ele2)); + return ele2.name.getText() === 'attributes'; + } + ) as ts.PropertyAssignment; + assert(ts.isArrayLiteralExpression(attrProperty.initializer)); + + const nameProperty = properties.find( + (ele2) => { + assert(ts.isPropertyAssignment(ele2)); + return ele2.name.getText() === 'name'; + } + ) as ts.PropertyAssignment; + assert(ts.isStringLiteral(nameProperty.initializer)); + const nameText = nameProperty.initializer.text; + if (indexNameDict[nameText]) { + throw new Error(`「${filename}」索引定义重名「${nameText}」`); + } + assign(indexNameDict, { + [nameText]: true, + }); + + const configProperty = properties.find( + (ele2) => { + assert(ts.isPropertyAssignment(ele2)); + return ele2.name.getText() === 'config'; + } + ) as ts.PropertyAssignment; + if (configProperty) { + assert(ts.isObjectLiteralExpression(configProperty.initializer)); + const { properties: properties2 } = configProperty.initializer; + const typeProperty = properties2.find( + (ele2) => { + assert(ts.isPropertyAssignment(ele2)); + return ele2.name.getText() === 'type'; + } + ) as ts.PropertyAssignment; + + if (typeProperty && (typeProperty.initializer!).text === 'fulltext') { + // 定义了全文索引 + if (hasFulltextIndex) { + throw new Error(`「${filename}」只能定义一个全文索引`); + } + hasFulltextIndex = true; + isFulltextIndex = true; + } + } + + const { elements } = attrProperty.initializer; + + // 每个属性都应该在schema中有值,且对象类型是可索引值 + elements.forEach( + (ele2) => { + assert(ts.isObjectLiteralExpression(ele2)); + const { properties: properties2 } = ele2; + + const nameProperty = properties2.find( + (ele3) => { + assert(ts.isPropertyAssignment(ele3)); + return ele3.name.getText() === 'name'; + } + ) as ts.PropertyAssignment; + + const indexAttrName = (nameProperty.initializer!).text; + if (!initinctiveAttributes.includes(indexAttrName)) { + const schemaNode = schemaAttrs.find( + (ele3) => { + assert(ts.isPropertySignature(ele3)); + return (ele3.name).text === indexAttrName; + } + ) as ts.PropertySignature; + if (!schemaNode) { + throw new Error(`「${filename}」中索引「${nameText}」的属性「${indexAttrName}」定义非法`); + } + + const { type, name } = schemaNode; + const entity = moduleName; + const { [entity]: manyToOneSet } = ManyToOne; + if (ts.isTypeReferenceNode(type!)) { + const { typeName } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity, attrName]) => refEntity === text2 && attrName === (name).text + ); + if (!manyToOneItem) { + // 如果不是外键,则不能是Text, File + if (isFulltextIndex) { + assert(['Text', 'String'].includes(text2), `「${filename}」中全文索引「${nameText}」定义的属性「${indexAttrName}」类型非法,只能是Text/String`); + } + else { + assert(!unIndexedTypes.includes(text2), `「${filename}」中索引「${nameText}」的属性「${indexAttrName}」的类型为「${text2}」,不可索引`); + } + } + else { + assert(!isFulltextIndex, `「${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型非法,只能为Text/String`); + // 在这里把外键加上Id,这样storageSchema才能正常通过 + // 这里的写法不太好,未来TS版本高了可能会有问题。by Xc 20230131 + Object.assign(nameProperty, { + initializer: factory.createStringLiteral(`${indexAttrName}Id`), + }); + } + } + else { + assert(false); // 这是什么case,不确定 + } + } + else { + assert(!isFulltextIndex, `「${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型只能为Text/String`); + assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}中索引「${nameText}」的属性${(name).text}有定义非法`); + } + } + } + ); + } + ); + + indexes = declaration.initializer; + } + else if (declaration.type && ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') { + // locale定义 + const { type, initializer } = declaration; + + assert(ts.isObjectLiteralExpression(initializer!)); + const { properties } = initializer; + assert(properties.length > 0, `${filename}至少需要有一种locale定义`); + + + const allEnumStringAttrs = Object.keys(enumAttributes); + const { typeArguments } = type; + assert(typeArguments && + ts.isTypeReferenceNode(typeArguments[0]) + && ts.isIdentifier(typeArguments[0].typeName) && typeArguments[0].typeName.text === 'Schema', `${filename}中缺少locale定义,或者locale类型定义的第一个参数不是Schema`); + + if (hasActionDef) { + assert(ts.isTypeReferenceNode(typeArguments[1]) + && ts.isIdentifier(typeArguments[1].typeName) && typeArguments[1].typeName.text === 'Action', `${filename}中locale类型定义的第二个参数不是Action`); + // 检查每种locale定义中都应该有'action'域 + checkLocaleExpressionPropertyExists(initializer, 'action', true, filename); + } + else { + assert(ts.isLiteralTypeNode(typeArguments[1]) + && ts.isStringLiteral(typeArguments[1].literal), `${filename}中locale类型定义的第二个参数不是字符串`); + checkLocaleExpressionPropertyExists(initializer, 'action', false, filename); + } + + if (hasRelationDef) { + assert(ts.isTypeReferenceNode(typeArguments[2]) + && ts.isIdentifier(typeArguments[2].typeName) + && typeArguments[2].typeName.text === 'Relation', `${filename}中的locale类型定义的第三个参数不是Relation`); + // 检查每种locale定义中都应该有'r'域 + checkLocaleExpressionPropertyExists(initializer, 'r', true, filename); + } + else { + assert(ts.isLiteralTypeNode(typeArguments[2]) + && ts.isStringLiteral(typeArguments[2].literal), `${filename}中locale类型定义的第三个参数不是空字符串`); + checkLocaleExpressionPropertyExists(initializer, 'r', false, filename); + } + + if (allEnumStringAttrs.length > 0) { + assert(ts.isTypeLiteralNode(typeArguments[3]), `${filename}中的locale类型定义的第四个参数不是{}`); + checkLocaleEnumAttrs(typeArguments[3], allEnumStringAttrs, filename); + // 检查每种locale定义中都应该有'v'域 + checkLocaleExpressionPropertyExists(initializer, 'v', true, filename); + } + else { + assert(ts.isTypeLiteralNode(typeArguments[3]), `${filename}中的locale类型定义的第四个参数不是{}`); + assert(typeArguments[3].members.length == 0, `${filename}中locale类型定义的第四个参数不应存在相应的v定义`) + // 检查每种locale定义中都应该有'v'域 + checkLocaleExpressionPropertyExists(initializer, 'v', false, filename); + } + + localeDef = initializer; + } + else if (declaration.type && ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') { + assert(!hasActionDef, `${moduleName}中的Configuration定义在Action之后`); + assert(ts.isObjectLiteralExpression(declaration.initializer!)); + const { properties } = declaration.initializer; + const atProperty = properties.find( + ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'actionType' + ); + const staticProperty = properties.find( + ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'static' + ); + if (atProperty) { + actionType = ((atProperty).initializer).text; + } + if (staticProperty) { + _static = true; // static如果有值只能为true + } + } + /* else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') { + // RelationHierary + assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`); + const { initializer } = declaration; + assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`); + relationHierarchy = initializer; + } + else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ReverseCascadeRelationHierarchy') { + // ReverseCascadeRelationHierarchy + assert(hasRelationDef, `${moduleName}中的Relation定义在ReverseCascadeRelationHierarchy之后`); + const { initializer } = declaration; + assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`); + reverseCascadeRelationHierarchy = initializer; + } */ + else { + throw new Error(`${moduleName}:不能理解的定义内容${(declaration.name as ts.Identifier).text}`); + } + } + ); + } + }); + + if (!hasActionDef && hasActionOrStateDef) { + throw new Error(`${filename}中有Action或State定义,但没有定义完整的Action类型`); + } + if (hasActionDef && actionType !== 'crud') { + throw new Error(`${filename}中有Action定义,但却定义了actionType不是crud`); + } + assert(schemaAttrs.length > 0, `对象${moduleName}没有任何属性定义`); + const schema = { + schemaAttrs, + sourceFile, + toModi, + actionType, + static: _static, + hasRelationDef, + enumAttributes, + additionalImports, + }; + if (hasFulltextIndex) { + assign(schema, { + fulltextIndex: true, + }); + } + if (indexes!) { + assign(schema, { + indexes, + }); + } + if (!localeDef) { + throw new Error(`${filename}中缺少了locale定义`); + } + else { + assign(schema, { + locale: localeDef, + }); + } + /* if (hasRelationDef) { + if(!relationHierarchy && !reverseCascadeRelationHierarchy){ + console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义,请注意自主编写权限分配的checker`); + } + if (relationHierarchy) { + assign(schema, { + relationHierarchy, + }); + } + if (reverseCascadeRelationHierarchy) { + assign(schema, { + reverseCascadeRelationHierarchy, + }); + } + } + else { + assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`); + assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`) + } */ + + assign(Schema, { + [moduleName]: schema, + }); +} + +/** + * 生成Schema + * @param statements + * @param schemaAttrs + * @param entity + */ +function constructSchema(statements: Array, entity: string) { + const { schemaAttrs } = Schema[entity]; + const members: Array = [ + ]; + const members2: Array = []; + + const { [entity]: manyToOneSet } = ManyToOne; + const { [entity]: oneToManySet } = OneToMany; + const referenceEntities: string[] = []; + for (const attr of schemaAttrs) { + const { type, name, questionToken } = attr; + const attrName = (name).text; + if (ts.isTypeReferenceNode(type!)) { + const { typeName } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity, attrName]) => refEntity === text2 && attrName === attrName + ); + if (manyToOneItem) { + referenceEntities.push(text2); + members2.push( + factory.createPropertySignature( + undefined, + name, + questionToken, + questionToken ? factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'Schema') + ), + factory.createLiteralTypeNode(factory.createNull()) + ]) : factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'Schema') + ) + ) + ); + const foreignKey = `${attrName}Id`; + members.push( + factory.createPropertySignature( + undefined, + factory.createIdentifier(foreignKey), + questionToken, + questionToken ? factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('ForeignKey'), + [ + factory.createLiteralTypeNode( + factory.createStringLiteral(firstLetterLowerCase(text2)) + ) + ] + ), + factory.createLiteralTypeNode(factory.createNull()) + ]) : factory.createTypeReferenceNode( + factory.createIdentifier('ForeignKey'), + [ + factory.createLiteralTypeNode( + factory.createStringLiteral(firstLetterLowerCase(text2)) + ) + ] + ) + ) + ); + } + else { + // assert(types.includes(text), `${entity}中的属性${name.toString()}有非法的属性类型定义`); + // 处理entity这种特殊情况 + if (ReversePointerRelations[entity] && attrName === 'entity') { + const entityUnionTypeNode: ts.TypeNode[] = ReversePointerRelations[entity].map( + ele => factory.createLiteralTypeNode( + factory.createStringLiteral(firstLetterLowerCase(ele)) + ) + ); + + if (process.env.COMPLING_AS_LIB) { + // 如果是建立 base-domain,还要容纳可能的其它对象引用 + entityUnionTypeNode.push( + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ) + } + members.push( + factory.createPropertySignature( + undefined, + name, + questionToken, + questionToken ? factory.createUnionTypeNode([ + factory.createUnionTypeNode( + entityUnionTypeNode + ), + factory.createLiteralTypeNode(factory.createNull()) + ]) : factory.createUnionTypeNode( + entityUnionTypeNode + ) + ) + ); + } + else { + members.push( + factory.createPropertySignature( + undefined, + name, + questionToken, + questionToken ? factory.createUnionTypeNode([ + type, + factory.createLiteralTypeNode(factory.createNull()) + ]) : type + ) + ); + } + } + } + else { + assert(false); // 这是什么case,不确定 + members.push(attr); + } + } + else { + assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}有非法的属性类型定义${(name).text}`); + members.push( + factory.createPropertySignature( + undefined, + name, + questionToken, + questionToken ? factory.createUnionTypeNode([ + type, + factory.createLiteralTypeNode(factory.createNull()) + ]) : type + ) + ); + } + } + // 处理reverserPointer + const reverseOnes = ReversePointerRelations[entity]; + if (reverseOnes) { + reverseOnes.forEach( + (one) => { + referenceEntities.push(one); + members2.push( + factory.createPropertySignature( + undefined, + firstLetterLowerCase(one), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'Schema') + ) + ) + ); + } + ) + } + + const foreignKeySet: Record = {}; + const otmMappedTypeNodes: ts.MappedTypeNode[] = []; + if (oneToManySet) { + for (const oneToManyItem of oneToManySet) { + const [entityName, foreignKey] = oneToManyItem; + if (referenceEntities.indexOf(entityName) === -1) { + referenceEntities.push(entityName); + } + + if (foreignKeySet.hasOwnProperty(entityName)) { + foreignKeySet[entityName].push(foreignKey); + } + else { + foreignKeySet[entityName] = [foreignKey]; + } + } + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + foreignKeySet[entityName].forEach( + (foreignKey) => { + const identifier = `${entityNameLc}$${foreignKey}`; + otmMappedTypeNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("A"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Schema'), + undefined + ) + ] + ), + undefined + ) + ); + /* members2.push( + factory.createPropertySignature( + undefined, + identifier, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Schema'), + undefined + )] + ), + ) + ); */ + const aggrIdentifier = `${entityNameLc}$${foreignKey}$$aggr`; + otmMappedTypeNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("A"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(aggrIdentifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("AggregationResult"), + [factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Schema'), + undefined + )] + ), + undefined + ) + ); + /* members2.push( + factory.createPropertySignature( + undefined, + aggrIdentifier, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("AggregationResult"), + [factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Schema'), + undefined + )] + ) + ) + ) */ + } + ); + } + } + + + uniq(referenceEntities).forEach( + (ele) => { + if (ele !== entity) { + statements.push(factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamespaceImport( + factory.createIdentifier(ele) + ) + ), + factory.createStringLiteral(`../${ele}/Schema`) + )); + } + } + ); + + // 在这里把需要直接拷贝过来的语句写入 + if (SchemaAsts[entity]) { + statements.push(...SchemaAsts[entity].statements); + } + + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [ + factory.createModifier(ts.SyntaxKind.ExportKeyword) + ], + factory.createIdentifier('OpSchema'), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeReferenceNode('EntityShape'), + factory.createTypeLiteralNode(members) + ]) + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("OpAttr"), + undefined, + factory.createTypeOperatorNode( + ts.SyntaxKind.KeyOfKeyword, + factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ) + ) + ) + ); + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [ + factory.createModifier(ts.SyntaxKind.ExportKeyword) + ], + factory.createIdentifier('Schema'), + undefined, + factory.createIntersectionTypeNode( + [ + factory.createTypeReferenceNode('EntityShape'), + factory.createTypeLiteralNode(members.concat(members2)), + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("A"), + factory.createTypeReferenceNode( + factory.createIdentifier("ExpressionKey"), + undefined + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + undefined + ), + ...otmMappedTypeNodes + ] + ) + ) + ); +} + +/** + * 生成Query + * @param statements + * @param schemaAttrs + * @param entity + */ +function constructFilter(statements: Array, entity: string) { + const { schemaAttrs, fulltextIndex } = Schema[entity]; + const members: Array = [ + // id: Q_StringValue + factory.createPropertySignature( + undefined, + factory.createIdentifier('id'), + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('Q_StringValue'), + ), + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier("SubQuery"), + factory.createIdentifier(`${entity}IdSubQuery`) + ) + ) + ]) + ), + // $$createAt$$: Q_DateValue + factory.createPropertySignature( + undefined, + factory.createIdentifier('$$createAt$$'), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('Q_DateValue'), + ) + ), + // $$seq$$: Q_StringValue + factory.createPropertySignature( + undefined, + factory.createIdentifier('$$seq$$'), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('Q_StringValue'), + ) + ), + // $$updateAt$$: Q_DateValue + factory.createPropertySignature( + undefined, + factory.createIdentifier('$$updateAt$$'), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('Q_DateValue'), + ) + ) + ]; + + const { [entity]: manyToOneSet } = ManyToOne; + + for (const attr of schemaAttrs) { + const { type, name } = attr; + const attrName = (name).text; + if (ts.isTypeReferenceNode(type!)) { + const { typeName } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + let type2: ts.TypeNode; + switch (text) { + case 'String': + case 'Text': + case 'Image': + case 'File': { + if (ReversePointerRelations[entity] && attrName === 'entity') { + type2 = factory.createTypeReferenceNode('E'); + } + else { + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Q_StringValue'), + ); + } + break; + } + case 'Int': + case 'Uint': + case 'Float': + case 'Double': + case 'Price': { + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Q_NumberValue'), + ); + break; + } + case 'Boolean': { + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Q_BooleanValue'), + ); + break; + } + case 'Datetime': { + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Q_DateValue'), + ); + break; + } + case 'SingleGeo': + case 'Geo': { + // geo类型暂时只支持通过expr查询 + break; + } + case 'Object': { + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Object'), + ); + break; + } + default: { + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity]) => refEntity === text2 + ); + if (manyToOneItem) { + // 外键可能落到相应的子查询中 + members.push( + factory.createPropertySignature( + undefined, + `${(name).text}Id`, + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('Q_StringValue'), + ), + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier("SubQuery"), + factory.createIdentifier(`${text2}IdSubQuery`) + ), + undefined + ) + ]) + ) + ); + type2 = factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'Filter') + ); + } + else { + // 这里应该都是引用某个UnionType类型的定义了,如何判断? + // const words = getStringTextFromUnionStringLiterals(); + type2 = factory.createTypeReferenceNode( + factory.createIdentifier('Q_EnumValue'), + [ + factory.createTypeReferenceNode( + factory.createIdentifier(text), + undefined + ) + ] + ); + } + } + } + if (type2!) { + members.push( + factory.createPropertySignature( + undefined, + name, + undefined, + type2 + ) + ); + } + } + } + else if (ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type!)) { + members.push( + factory.createPropertySignature( + undefined, + name, + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('Q_EnumValue'), + [ + type + ] + ) + ) + ); + } + else { + // 此时应当是引用本地定义的shape + } + } + + // type AttrFilter = {}; + if (ReversePointerRelations[entity]) { + // 有反向指针,将反向指针关联的对象的Filter也注入 + ReversePointerRelations[entity].forEach( + ele => + members.push( + factory.createPropertySignature( + undefined, + firstLetterLowerCase(ele), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, ele, 'Filter') + ) + ) + ) + ); + } + const eumUnionTypeNode: ts.TypeNode[] = ReversePointerRelations[entity] && ReversePointerRelations[entity].map( + ele => factory.createLiteralTypeNode( + factory.createStringLiteral(firstLetterLowerCase(ele)) + ) + ); + if (process.env.COMPLING_AS_LIB) { + eumUnionTypeNode && eumUnionTypeNode.push( + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ); + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + undefined, + factory.createIdentifier('AttrFilter'), + ReversePointerRelations[entity] ? [ + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("E"), + undefined, + ) + ] : undefined, + factory.createTypeLiteralNode(members) + ) + ); + + /** + * + export type Filter = AttrFilter | Partial | { + [F in Q_LogicKey]: Filter[]; + } | { + [F in Q_FullTextKey]: Q_FullTextValue; + }>; + + */ + const types: ts.TypeNode[] = [ + factory.createTypeReferenceNode( + factory.createIdentifier("AttrFilter"), + ReversePointerRelations[entity] ? [factory.createTypeReferenceNode('E')] : undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("ExprOp"), + [ + process.env.COMPLING_AS_LIB ? + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ]) : + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ) + ] + ), + ]; + + // 如果还有其它类型的查询如全文,则加在types数组中 + if (fulltextIndex) { + types.push( + factory.createTypeReferenceNode('FulltextFilter') + ); + } + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Filter"), + ReversePointerRelations[entity] ? [ + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("E"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Q_EnumValue"), + [ + factory.createUnionTypeNode( + eumUnionTypeNode + ) + ] + ) + ) + ] : undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("MakeFilter"), + [factory.createIntersectionTypeNode(types)] + ) + ) + ); +} + +/** + * 构造Projection和OneAttrProjection + * @param statements + * @param entity + */ +function constructProjection(statements: Array, entity: string) { + const { schemaAttrs } = Schema[entity]; + const properties: Array<[string | ts.PropertyName, boolean, ts.TypeNode?/* , ts.TypeNode? */]> = [ + ['id', false], + ['$$createAt$$', false], + ['$$updateAt$$', false], + ['$$seq$$', false], + ]; + const foreignKeyProperties: { + [k: string]: [string] + } = { + [entity]: [''], + }; + + const { [entity]: manyToOneSet } = ManyToOne; + + for (const attr of schemaAttrs) { + const { type, name } = attr; + const attrName = (name).text; + if (ts.isTypeReferenceNode(type!)) { + const { typeName } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + switch (text) { + case 'String': + case 'Text': + case 'Int': + case 'Float': + case 'Double': + case 'Boolean': + case 'Datetime': + case 'Image': + case 'File': + case 'SingleGeo': + case 'Geo': + case 'Object': + case 'Price': { + properties.push( + [name, false] + ) + break; + } + default: { + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity]) => refEntity === text2 + ); + if (manyToOneItem) { + // 外键投影 + properties.push( + [`${attrName}Id`, false, undefined], + [name, false, factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'Projection') + )/* , factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'ExportProjection') + ) */] + ); + if (foreignKeyProperties.hasOwnProperty(text2)) { + foreignKeyProperties[text2].push(attrName); + } + else { + assign(foreignKeyProperties, { + [text2]: [attrName], + }); + } + } + else { + // todo 此处是对State的专门处理 + if (text.endsWith('State')) { + properties.push( + [name, false, undefined] + ); + } + else { + // 引用的shape + properties.push( + [name, false, undefined] + ); + } + } + } + } + } + else { + assert(false); + } + } + else { + // 增加了本身object的shape定义 + // assert(ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type!)); + properties.push( + [name, false, undefined] + ) + } + } + + if (ReversePointerRelations[entity]) { + ReversePointerRelations[entity].forEach( + (one) => { + const text2 = one === 'Schema' ? entity : one; + properties.push( + [firstLetterLowerCase(one), false, factory.createTypeReferenceNode( + createForeignRef(entity, one, 'Projection') + )/* , factory.createTypeReferenceNode( + createForeignRef(entity, one, 'ExportProjection') + ) */] + ); + if (foreignKeyProperties.hasOwnProperty(one)) { + foreignKeyProperties[text2].push('entity'); + } + else { + assign(foreignKeyProperties, { + [text2]: ['entity'], + }); + } + } + ) + } + + // 一对多的projection + const { [entity]: oneToManySet } = OneToMany; + const otmMappedTypeNodes: ts.MappedTypeNode[] = []; + if (oneToManySet) { + const foreignKeySet: Record = {}; + for (const oneToManyItem of oneToManySet) { + const [entityName, foreignKey] = oneToManyItem; + + if (foreignKeySet.hasOwnProperty(entityName)) { + foreignKeySet[entityName].push(foreignKey); + } + else { + foreignKeySet[entityName] = [foreignKey]; + } + } + + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + foreignKeySet[entityName].forEach( + (foreignKey) => { + const identifier = `${entityNameLc}$${foreignKey}`; + const aggrIdentifier = `${entityNameLc}$${foreignKey}$$aggr`; + otmMappedTypeNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("A"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createIntersectionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Selection'), + undefined + ), + factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("$entity"), + undefined, + factory.createLiteralTypeNode(factory.createStringLiteral(firstLetterLowerCase(entityName))) + ) + ]) + ]), + undefined + ), + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("A"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(aggrIdentifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createIntersectionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Aggregation'), + undefined + ), + factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("$entity"), + undefined, + factory.createLiteralTypeNode(factory.createStringLiteral(firstLetterLowerCase(entityName))) + ) + ]) + ]), + undefined + ), + ); + } + ); + } + } + + const exprNode = factory.createTypeReferenceNode( + factory.createIdentifier("Partial"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("ExprOp"), + [ + process.env.COMPLING_AS_LIB ? + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ]) : + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ) + ] + ) + ] + ); + + const MetaPropertySignatures: ts.TypeElement[] = [ + factory.createPropertySignature( + undefined, + factory.createStringLiteral("#id"), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + 'NodeId' + ) + ) + ]; + if (process.env.COMPLING_AS_LIB) { + MetaPropertySignatures.push( + factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("k"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ) + } + // Projection,正常查询的投影 + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Projection"), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeLiteralNode( + MetaPropertySignatures.concat( + properties.map( + ([n, q, v]) => { + return factory.createPropertySignature( + undefined, + n, + q ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken), + v || factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + ) + } + ) + ) + ), + exprNode, + ...otmMappedTypeNodes + ]) + ) + ); + + // ExportProjection,下载查询的投影 + // 已经废弃。By Xc 2023.01.08 + /* statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("ExportProjection"), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeLiteralNode( + MetaPropertySignaturs.concat( + properties.map( + ([n, q, v, v2]) => { + return factory.createPropertySignature( + undefined, + n, + factory.createToken(ts.SyntaxKind.QuestionToken), + v2 || factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ) + } + ) + ) + ), + exprNode, + ]) + ) + ); */ + + // ${Entity}Projection,外键查询的专用投影 + for (const foreignKey in foreignKeyProperties) { + const identifier = `${foreignKey}IdProjection`; + statements.push( + factory.createTypeAliasDeclaration( + undefined, + undefined, + factory.createIdentifier(identifier), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OneOf"), + [ + factory.createTypeLiteralNode( + foreignKeyProperties[foreignKey].map( + (attr) => factory.createPropertySignature( + undefined, + attr ? factory.createIdentifier(`${attr}Id`) : 'id', + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + ) + ) + ) + ] + ) + ) + ); + } +} + +/** + * 构造Query + * @param statements + * @param entity + */ +function constructQuery(statements: Array, entity: string) { + const entityLc = firstLetterLowerCase(entity); + + /* statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Query"), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeLiteralNode([ + // 这里可以不写entity了 + factory.createPropertySignature( + undefined, + factory.createIdentifier("projection"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Projection"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("sort"), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ) + ]), + factory.createTypeReferenceNode( + factory.createIdentifier("OakFilter"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("select")), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ) + ] + ), + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("Operation"), + undefined + ) + ] + ) + ]) + ) + ); */ + + /** + * export type ExportQuery = { + entity: 'user'; + projection: ExportProjection; + filter?: Filter; + sort?: Sorter; + indexFrom?: number; + count?: number; + }; + */ + /* statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("ExportQuery"), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("projection"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("ExportProjection"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("sort"), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ) + ]), + factory.createTypeReferenceNode( + factory.createIdentifier("OakFilter"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("select")), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ) + ] + ) + ]) + ) + ); */ + + // 对每个可能的外键的子查询,建立相应的${Entity}IdSubQuery + const { [entity]: manyToOneSet } = ManyToOne; + let manyToSelf = false; + if (manyToOneSet) { + uniqBy(manyToOneSet, ([a]) => a).forEach( + ([oneEntity, foreignKey]) => { + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier(`${oneEntity}IdSubQuery`), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Selection"), + [factory.createTypeReferenceNode( + factory.createIdentifier(`${oneEntity}IdProjection`), + undefined + )] + ) + ) + ); + + if (oneEntity === entity) { + manyToSelf = true; + } + } + ); + } + + // 主键可能产生的子查询 + if (!manyToSelf) { + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier(`${entity}IdSubQuery`), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Selection"), + [factory.createTypeReferenceNode( + factory.createIdentifier(`${entity}IdProjection`), + undefined + )] + ) + ) + ); + } +} + +/** + * 构造Sort + * @param statements + * @param entity + */ +function constructSorter(statements: Array, entity: string) { + const { schemaAttrs } = Schema[entity]; + const members: Array = [ + // id: 1 + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + factory.createIdentifier("id"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ), + // $$createAt$$: 1 + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + factory.createIdentifier("$$createAt$$"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ), + // $$seq$$: 1 + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + factory.createIdentifier("$$seq$$"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ), + // $$updateAt$$: 1 + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + factory.createIdentifier("$$updateAt$$"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ), + ]; + + const { [entity]: manyToOneSet } = ManyToOne; + + for (const attr of schemaAttrs) { + const { type, name, questionToken } = attr; + if (ts.isTypeReferenceNode(type!)) { + const { typeName } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + let type2: ts.TypeNode; + switch (text) { + case 'String': + case 'Text': + case 'Int': + case 'Float': + case 'Double': + case 'Boolean': + case 'Datetime': + case 'Image': + case 'File': + case 'Price': { + type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + break; + } + default: { + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity]) => refEntity === text2 + ); + if (manyToOneItem) { + type2 = factory.createTypeReferenceNode( + createForeignRef(entity, text2, 'SortAttr') + ); + + members.push( + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + factory.createIdentifier(`${(name).text}Id`), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ) + ); + } + else if (!['Object'].includes(text)) { + // todo 对State的专门处理 + type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + } + } + } + if (type2!) { + members.push( + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + name, + undefined, + type2 + )] + ) + ); + } + } + } + else if (ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type!)) { + members.push( + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + name, + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + )] + ) + ); + } + else { + // 本地规定的shape,非结构化属性不参与排序 + } + } + + if (ReversePointerRelations[entity]) { + ReversePointerRelations[entity].forEach( + (one) => { + members.push( + factory.createTypeLiteralNode( + [factory.createPropertySignature( + undefined, + firstLetterLowerCase(one), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'SortAttr') + ) + )] + ) + ); + } + ); + } + + if (process.env.COMPLING_AS_LIB) { + members.push( + factory.createTypeLiteralNode([factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("k"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + )]) + ); + } + + members.push( + factory.createTypeReferenceNode( + factory.createIdentifier("OneOf"), + [factory.createTypeReferenceNode( + factory.createIdentifier("ExprOp"), + [ + process.env.COMPLING_AS_LIB ? + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ]) : + factory.createTypeReferenceNode( + factory.createIdentifier('OpAttr') + ) + ] + )] + ) + ); + /** + * + export type SortAttr = { + id: 1; + } | { + $$createAt$$: 1; + } | { + $$updateAt$$: 1; + } | { + modiId: 1; + } | { + modi: Modi.SortAttr; + } | { + [k: string]: any; + } | OneOf> + */ + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("SortAttr"), + undefined, + factory.createUnionTypeNode(members) + ) + ); + + /** + * export type SortNode = { + $attr: SortAttr; + $direction?: 'asc' | 'desc'; + }; + */ + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("SortNode"), + undefined, + factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("$attr"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("SortAttr"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("$direction"), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + factory.createLiteralTypeNode(factory.createStringLiteral("asc")), + factory.createLiteralTypeNode(factory.createStringLiteral("desc")) + ]) + ) + ]) + ) + ); + + /** + * export type Sorter = SortNode[]; + */ + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Sorter"), + undefined, + factory.createArrayTypeNode(factory.createTypeReferenceNode( + factory.createIdentifier("SortNode"), + undefined + )) + ) + ) +} + +function constructFullAttrs(statements: Array, entity: string) { + const { [entity]: manyToOneSet } = ManyToOne; + const { [entity]: oneToManySet } = OneToMany; + + if (manyToOneSet && manyToOneSet.length) { + const mtoAttrs: ts.TypeNode[] = []; + for (const item of manyToOneSet) { + const [one, key] = item; + if (one === entity) { + // 递归引用自身,因为typescript本身不支持递归,因此这里做一个显式的三层递归应该够用了 + mtoAttrs.push( + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${key}.`, + `${key}.` + ), + [factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + factory.createIdentifier("OpAttr"), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + )] + ), + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${key}.${key}.`, + `${key}.${key}.` + ), + [factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + factory.createIdentifier("OpAttr"), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + )] + ), + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${key}.${key}.${key}.`, + `${key}.${key}.${key}.` + ), + [factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + factory.createIdentifier("OpAttr"), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + )] + ) + ) + } + else { + mtoAttrs.push( + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${key}.`, + `${key}.` + ), + [factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier(one), + factory.createIdentifier("NativeAttr") + ), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + ) + ] + ) + ); + } + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("NativeAttr"), + undefined, + factory.createUnionTypeNode( + [ + factory.createTypeReferenceNode( + factory.createIdentifier("OpAttr"), + undefined + ) + , + ...mtoAttrs + ] + ) + ) + ); + } + else { + statements.push(factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("NativeAttr"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OpAttr"), + undefined + ) + )); + } + + const foreignKeySet: Record = {}; + if (oneToManySet && oneToManySet.length > 0) { + for (const oneToManyItem of oneToManySet) { + const [entityName, foreignKey] = oneToManyItem; + if (foreignKeySet.hasOwnProperty(entityName)) { + foreignKeySet[entityName].push(foreignKey); + } + else { + foreignKeySet[entityName] = [foreignKey]; + } + } + const otmAttrs: ts.TemplateLiteralTypeNode[] = []; + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + if (foreignKeySet[entityName].length > 1) { + foreignKeySet[entityName].forEach( + (foreignKey) => { + const head = `${entityNameLc}s$${foreignKey}`; + otmAttrs.push( + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${head}$`, + `${head}$` + ), + [ + factory.createTemplateLiteralTypeSpan( + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), + factory.createTemplateMiddle( + ".", + "." + ) + ), + factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + entityName === entity + ? factory.createIdentifier("NativeAttr") + : factory.createQualifiedName( + factory.createIdentifier(entityName), + factory.createIdentifier("NativeAttr") + ), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + ) + ] + ) + ); + } + ); + } + else { + otmAttrs.push( + factory.createTemplateLiteralType( + factory.createTemplateHead( + `${entityNameLc}s$`, + `${entityNameLc}s$` + ), + [ + factory.createTemplateLiteralTypeSpan( + factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), + factory.createTemplateMiddle( + ".", + "." + ) + ), + factory.createTemplateLiteralTypeSpan( + factory.createTypeReferenceNode( + entityName === entity + ? factory.createIdentifier("NativeAttr") + : factory.createQualifiedName( + factory.createIdentifier(entityName), + factory.createIdentifier("NativeAttr") + ), + undefined + ), + factory.createTemplateTail( + "", + "" + ) + ) + ] + ) + ); + } + } + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("FullAttr"), + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier("NativeAttr"), + undefined + ), + ...otmAttrs + ]) + ) + ); + } + else { + statements.push(factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("FullAttr"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("NativeAttr"), + undefined + ) + )); + } + +} + +function constructActions(statements: Array, entity: string) { + // Selection + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("SelectOperation"), + [ + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("P"), + factory.createTypeReferenceNode( + factory.createIdentifier("Object"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Projection"), + undefined + ) + ) + ], + factory.createTypeReferenceNode( + factory.createIdentifier("OakSelection"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("select")), + factory.createTypeReferenceNode( + factory.createIdentifier("P"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ] + ) + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Selection"), + [ + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("P"), + factory.createTypeReferenceNode( + factory.createIdentifier("Object"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Projection"), + undefined + ) + ) + ], + factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("SelectOperation"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("P"), + undefined + ) + ] + ), + factory.createLiteralTypeNode(factory.createStringLiteral("action")) + ] + ) + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createToken(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Aggregation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("DeduceAggregation"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("Projection"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ] + ) + ) + ); + + // Exportation + // 已经废弃,by Xc 2023.01.08 + /* statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Exportation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("export")), + factory.createTypeReferenceNode( + factory.createIdentifier("ExportProjection"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ] + ) + ) + ); */ + + const { [entity]: manyToOneSet } = ManyToOne; + const { [entity]: oneToManySet } = OneToMany; + const foreignKeySet: Record = {}; + if (oneToManySet) { + for (const oneToManyItem of oneToManySet) { + const [entityName, foreignKey] = oneToManyItem; + + if (foreignKeySet.hasOwnProperty(entityName)) { + foreignKeySet[entityName].push(foreignKey); + } + else { + foreignKeySet[entityName] = [foreignKey]; + } + } + } + // CreateOperationData + let foreignKeyAttr: string[] = []; + + if (ReversePointerEntities[entity]) { + foreignKeyAttr.push( + 'entity', 'entityId' + ); + } + if (manyToOneSet) { + for (const one of manyToOneSet) { + if (!ReversePointerRelations[entity] || !ReversePointerRelations[entity].includes(one[1])) { + foreignKeyAttr.push(`${one[1]}Id`); + } + } + } + let adNodes: ts.TypeNode[] = [ + factory.createTypeReferenceNode( + factory.createIdentifier("FormCreateData"), + [ + foreignKeyAttr.length > 0 + ? factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ), + factory.createUnionTypeNode(uniq(foreignKeyAttr).map( + ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele)) + )) + ] + ) + : factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ) + ] + ) + ]; + + + if (manyToOneSet) { + /** + * create的多对一有两种case + * 如果关联对象是create,则对象的外键由关联对象的id决定 + * 如果关联对象是update,则关联对象的filter由对象决定其主键 + * 见cascadeStore + */ + const upsertOneNodes: ts.TypeNode[] = []; + for (const one of manyToOneSet) { + if (!ReversePointerRelations[entity] || !ReversePointerRelations[entity].includes(one[0])) { + const oneEntity = one[0]; + const cascadeCreateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + one[2] ? factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'CreateSingleOperation') + ) + ) + ] + ); + const cascadeUpdateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'UpdateOperation') + ) + ) + ] + ); + const noCascadeNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + one[2] ? factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ) + ) + ] + ); + if (Schema[oneEntity].static) { + upsertOneNodes.push(noCascadeNode); + } + else { + switch (Schema[oneEntity].actionType) { + case 'crud': + case 'excludeRemove': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, noCascadeNode]) + ); + break; + } + case 'excludeUpdate': + case 'appendOnly': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, noCascadeNode]) + ); + break; + } + case 'readOnly': { + upsertOneNodes.push(noCascadeNode); + break; + } + default: { + assert(false); + } + } + } + } + } + + if (upsertOneNodes.length > 0) { + adNodes.push( + factory.createIntersectionTypeNode( + upsertOneNodes + ) + ); + } + } + const reverseOneNodes: ts.TypeNode[] = []; + if (ReversePointerEntities[entity]) { + if (ReversePointerRelations[entity]) { + const { schemaAttrs } = Schema[entity]; + const { questionToken } = schemaAttrs.find( + ele => { + const { name } = ele; + return (name).text === 'entity' + } + )!; + for (const one of ReversePointerRelations[entity]) { + const cascadeCreateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'CreateSingleOperation') + ) + ) + ] + ); + const cascadeUpdateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc + factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`) + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + undefined, // 反向指针好像不能为空,以后或许会有特例 by Xc + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'UpdateOperation') + ) + ) + ] + ); + const noCascadeNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + questionToken, + factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`) + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + questionToken, + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ) + ) + ] + ); + if (Schema[one].static) { + reverseOneNodes.push(noCascadeNode); + } + else { + switch (Schema[one].actionType) { + case 'crud': + case 'excludeRemove': { + reverseOneNodes.push(cascadeCreateNode, cascadeUpdateNode, noCascadeNode); + break; + } + case 'appendOnly': + case 'excludeUpdate': { + reverseOneNodes.push(cascadeCreateNode, noCascadeNode); + break; + } + case 'readOnly': { + reverseOneNodes.push(noCascadeNode); + break; + } + default: { + assert(false); + } + } + } + } + } + + if (process.env.COMPLING_AS_LIB) { + // 如果是base,要包容更多可能的反指 + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ), + factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("K"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ] + ) + ); + } + + if (reverseOneNodes.length > 0) { + adNodes.push( + factory.createUnionTypeNode( + reverseOneNodes + ) + ); + } + } + + // 一对多 + if (oneToManySet) { + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + foreignKeySet[entityName].forEach( + (foreignKey) => { + const identifier = `${entityNameLc}$${foreignKey}`; + const otmCreateOperationDataNode = factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'CreateOperationData'), + undefined + ), + factory.createUnionTypeNode(foreignKey === 'entity' ? [ + factory.createLiteralTypeNode(factory.createStringLiteral("entity")), + factory.createLiteralTypeNode(factory.createStringLiteral("entityId")) + ] : [ + factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)), + factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`)) + ]) + ] + ); + const otmCreateSingleOperationNode = factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + otmCreateOperationDataNode + ] + ); + const otmCreateMultipleOperationNode = factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + factory.createArrayTypeNode( + otmCreateOperationDataNode + ) + ] + ); + const otmUpdateOperationNode = factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createIndexedAccessTypeNode( + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'UpdateOperation'), + undefined + ), + factory.createLiteralTypeNode(factory.createStringLiteral("action")) + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'UpdateOperationData'), + undefined + ), + factory.createUnionTypeNode(foreignKey === 'entity' ? [ + factory.createLiteralTypeNode(factory.createStringLiteral("entity")), + factory.createLiteralTypeNode(factory.createStringLiteral("entityId")) + ] : [ + factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)), + factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`)) + ]) + ] + ), + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'Filter'), + undefined + ) + ] + ); + + if (!Schema[entityName].static) { + switch (Schema[entityName].actionType) { + case 'crud': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmUpdateOperationNode, + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + otmCreateSingleOperationNode, + otmUpdateOperationNode + ])] + ) + ]), + undefined + ) + ); + break; + } + case 'appendOnly': + case 'excludeUpdate': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [otmCreateSingleOperationNode] + ) + ]), + undefined + ) + ); + break; + } + case 'readOnly': { + break; + } + default: { + assert(false); + } + } + } + } + ); + } + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("CreateOperationData"), + undefined, + factory.createIntersectionTypeNode(adNodes) + ) + ); + + // CreateOperation + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("CreateSingleOperation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + factory.createTypeReferenceNode( + factory.createIdentifier("CreateOperationData") + ) + ] + ) + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("CreateMultipleOperation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createTypeReferenceNode( + factory.createIdentifier("CreateOperationData") + )] + ) + ] + ) + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("CreateOperation"), + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier("CreateSingleOperation") + ), + factory.createTypeReferenceNode( + factory.createIdentifier("CreateMultipleOperation") + ) + ]) + ) + ); + + // UpdateOperationData + foreignKeyAttr = []; + if (ReversePointerRelations[entity]) { + foreignKeyAttr.push('entity', 'entityId'); + } + if (manyToOneSet) { + for (const one of manyToOneSet) { + if (!ReversePointerRelations[entity] || !ReversePointerRelations[entity].includes(one[1])) { + foreignKeyAttr.push(`${one[1]}Id`); + } + } + } + adNodes = [ + factory.createTypeReferenceNode( + factory.createIdentifier("FormUpdateData"), + [ + foreignKeyAttr.length > 0 ? factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ), + factory.createUnionTypeNode(uniq(foreignKeyAttr).map( + ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele)) + )) + ] + ) : factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ) + ] + ) + ]; + if (manyToOneSet) { + /** + * update的多对一有三种case + * 如果关联对象是create,则对象的外键由关联对象的id决定 + * 如果关联对象是update或者remove,则关联对象的filter由对象(的原行!注意这里的外键是不能变的!)决定其主键 + * 见cascadeStore + */ + const upsertOneNodes: ts.TypeNode[] = []; + for (const one of manyToOneSet) { + if (!ReversePointerRelations[entity] || !ReversePointerRelations[entity].includes(one[0])) { + const cascadeCreateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'CreateSingleOperation') + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + ] + ); + const cascadeUpdateNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'UpdateOperation') + ), + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + ] + ); + const cascadeRemoveNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + undefined, + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'RemoveOperation') + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + ] + ); + const noCascadeNode = factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier(`${one[1]}Id`), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode( + [ + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ), + factory.createLiteralTypeNode(factory.createNull()) + ] + ) + ), + ] + ); + if (Schema[one[0]].static) { + upsertOneNodes.push( + noCascadeNode + ); + } + else { + switch (Schema[one[0]].actionType) { + case 'crud': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, cascadeRemoveNode, noCascadeNode]) + ); + break; + } + case 'excludeUpdate': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, cascadeRemoveNode, noCascadeNode]) + ); + break; + } + case 'appendOnly': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, noCascadeNode]) + ); + break; + } + case 'readOnly': { + upsertOneNodes.push( + noCascadeNode + ); + break; + } + case 'excludeRemove': { + upsertOneNodes.push( + factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, noCascadeNode]) + ); + break; + } + default: { + assert(false); + } + } + } + } + } + if (upsertOneNodes.length > 0) { + adNodes.push( + factory.createIntersectionTypeNode( + upsertOneNodes + ) + ); + } + + const reverseOneNodes: ts.TypeNode[] = []; + if (ReversePointerRelations[entity]) { + const refEntityLitrals: (ts.TypeNode)[] = []; + for (const one of ReversePointerRelations[entity]) { + refEntityLitrals.push(factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`))); + const actionNodes: ts.TypeNode[] = []; + if (!Schema[one].static) { + switch (Schema[one].actionType) { + case 'crud': { + actionNodes.push( + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'CreateSingleOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'UpdateOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'RemoveOperation') + ) + ); + break; + } + case 'excludeUpdate': { + actionNodes.push( + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'CreateSingleOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'RemoveOperation') + ) + ); + break; + } + case 'excludeRemove': { + actionNodes.push( + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'CreateSingleOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'UpdateOperation') + ) + ); + break; + } + case 'appendOnly': { + actionNodes.push( + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'CreateSingleOperation') + ) + ); + break; + } + case 'readOnly': { + break; + } + default: { + assert(false); + } + } + } + if (actionNodes.length > 0) { + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode(actionNodes) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) + ) + ] + ), + ); + } + } + if (process.env.COMPLING_AS_LIB) { + // 如果是base,要包容更多可能的反指 + refEntityLitrals.push( + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ); + } + + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier('entity'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode( + [ + factory.createUnionTypeNode(refEntityLitrals), + factory.createLiteralTypeNode(factory.createNull()) + ] + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier('entityId'), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode( + [ + factory.createTypeReferenceNode( + factory.createIdentifier("String"), + [factory.createLiteralTypeNode(factory.createNumericLiteral("64"))] + ), + factory.createLiteralTypeNode(factory.createNull()) + ] + ) + ) + ] + ) + ); + } + + if (reverseOneNodes.length > 0) { + adNodes.push( + factory.createUnionTypeNode( + reverseOneNodes + ) + ); + } + } + + if (oneToManySet) { + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + foreignKeySet[entityName].forEach( + (foreignKey) => { + const identifier = `${entityNameLc}$${foreignKey}`; + + const otmCreateOperationDataNode = factory.createTypeReferenceNode( + factory.createIdentifier("Omit"), + [ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'CreateOperationData'), + undefined + ), + factory.createUnionTypeNode(foreignKey === 'entity' ? [ + factory.createLiteralTypeNode(factory.createStringLiteral("entity")), + factory.createLiteralTypeNode(factory.createStringLiteral("entityId")) + ] : [ + factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)), + factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`)) + ]) + ] + ); + const otmCreateSingleOperationNode = factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + otmCreateOperationDataNode + ] + ) + const otmCreateMultipleOperationNode = factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("create")), + factory.createArrayTypeNode( + otmCreateOperationDataNode + ) + ] + ); + const otmUpdateOperationNode = factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'UpdateOperation'), + undefined + ); + const otmRemoveOperationNode = factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'RemoveOperation'), + undefined + ); + if (!Schema[entityName].static) { + switch (Schema[entityName].actionType) { + case 'crud': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmUpdateOperationNode, + otmRemoveOperationNode, + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + otmCreateSingleOperationNode, + otmUpdateOperationNode, + otmRemoveOperationNode + ])] + ) + ]), + undefined + ) + ); + break; + } + case 'excludeUpdate': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmRemoveOperationNode, + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + otmCreateSingleOperationNode, + otmRemoveOperationNode + ])] + ) + ]), + undefined + ) + ); + break; + } + case 'excludeRemove': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmUpdateOperationNode, + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + otmCreateSingleOperationNode, + otmUpdateOperationNode + ])] + ) + ]), + undefined + ) + ); + break; + } + case 'appendOnly': { + adNodes.push( + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("X"), + factory.createTypeReferenceNode( + factory.createIdentifier("OtmKey"), + [factory.createLiteralTypeNode(factory.createStringLiteral(identifier))] + ), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + otmCreateMultipleOperationNode, + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + otmCreateSingleOperationNode + ])] + ) + ]), + undefined + ) + ); + break; + } + case 'readOnly': { + break; + } + default: { + assert(false); + } + } + } + } + ); + } + } + + if (process.env.COMPLING_AS_LIB) { + adNodes.push( + factory.createTypeLiteralNode( + [factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("k"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + )] + ) + ); + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("UpdateOperationData"), + undefined, + factory.createIntersectionTypeNode(adNodes) + ) + ); + + // UpdateOperation + const actionTypeNodes: ts.TypeNode[] = [factory.createLiteralTypeNode(factory.createStringLiteral("update"))]; + + if (ActionAsts[entity]) { + actionTypeNodes.push( + factory.createTypeReferenceNode('ParticularAction') + ); + } + if (Schema[entity].hasRelationDef || entity === 'User') { + actionTypeNodes.push( + factory.createTypeReferenceNode('RelationAction') + ); + } + if (process.env.COMPLING_AS_LIB) { + actionTypeNodes.push( + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ); + } + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("UpdateOperation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createUnionTypeNode(actionTypeNodes), + factory.createTypeReferenceNode( + factory.createIdentifier("UpdateOperationData") + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ] + ) + ) + ); + + // RemoveOperationData + adNodes = [ + factory.createTypeLiteralNode([]) + ]; + if (manyToOneSet) { + /** + * remove的多对一有两种case + * 如果关联对象动作是update或者remove,则关联对象的filter由对象(的原行!注意这里的外键是不能变的!)决定其主键 + * 见cascadeStore + */ + const upsertOneNodes: ts.TypeNode[] = []; + for (const one of manyToOneSet) { + if (!ReversePointerRelations[entity] || !ReversePointerRelations[entity].includes(one[0])) { + if (!Schema[one[0]].static) { + switch (Schema[one[0]].actionType) { + case 'crud': { + upsertOneNodes.push( + factory.createUnionTypeNode( + [ + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'UpdateOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'RemoveOperation') + ) + ]) + ) + ] + ) + ] + ) + ); + break; + } + case 'excludeUpdate': { + upsertOneNodes.push( + factory.createUnionTypeNode( + [ + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'RemoveOperation') + ) + ) + ] + ) + ] + ) + ); + break; + } + case 'excludeRemove': { + upsertOneNodes.push( + factory.createUnionTypeNode( + [ + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(one[1]), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one[0], 'UpdateOperation') + ) + ) + ] + ) + ] + ) + ); + break; + } + case 'appendOnly': + case 'readOnly': { + break; + } + default: { + assert(false); + } + } + } + } + } + + const reverseOneNodes: ts.TypeNode[] = []; + if (ReversePointerRelations[entity]) { + const refEntityLitrals: (ts.TypeNode)[] = []; + for (const one of ReversePointerRelations[entity]) { + refEntityLitrals.push(factory.createLiteralTypeNode(factory.createStringLiteral(`${firstLetterLowerCase(one)}`))); + if (!Schema[one].static) { + switch (Schema[one].actionType) { + case 'crud': { + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'UpdateOperation') + ), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'RemoveOperation') + ) + ]) + ) + ] + ), + ); + break; + } + case 'excludeUpdate': { + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'RemoveOperation') + ) + ) + ] + ), + ); + break; + } + case 'excludeRemove': { + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier(firstLetterLowerCase(one)), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createTypeReferenceNode( + createForeignRef(entity, one, 'UpdateOperation') + ) + ) + ] + ), + ); + break; + } + case 'appendOnly': + case 'readOnly': { + break; + } + default: { + assert(false); + } + } + } + + } + if (process.env.COMPLING_AS_LIB) { + reverseOneNodes.push( + factory.createTypeLiteralNode( + [ + factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("k"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ] + ) + ) + } + } + + if (upsertOneNodes.length > 0) { + adNodes.push( + factory.createIntersectionTypeNode( + upsertOneNodes + ) + ); + } + if (reverseOneNodes.length > 0) { + adNodes.push( + factory.createUnionTypeNode( + reverseOneNodes + ) + ); + } + } + + /** + * remove的同时进行cascade update或者cascade remove,感觉用触发器会更自然,因为在用户界面上似乎不会有对应的操作。 + * 这部分代码暂时封闭 by Xc 20220501 + **/ + + /* const propertySignatures3: ts.TypeElement[] = []; + if (process.env.COMPLING_AS_LIB) { + propertySignatures3.push( + factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("k"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ); + } + if (oneToManySet) { + for (const entityName in foreignKeySet) { + const entityNameLc = firstLetterLowerCase(entityName); + foreignKeySet[entityName].forEach( + (foreignKey) => { + const identifier = `${entityNameLc}s$${foreignKey}`; + propertySignatures3.push( + factory.createPropertySignature( + undefined, + factory.createIdentifier(identifier), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'UpdateOperation'), + undefined + ), + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'RemoveOperation'), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Array"), + [factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'UpdateOperation'), + undefined + ), + factory.createTypeReferenceNode( + createForeignRef(entity, entityName, 'RemoveOperation'), + undefined + ) + ])] + ) + ]) + ) + ); + } + ); + } + } + if (propertySignatures3.length > 0) { + adNodes.push( + factory.createTypeLiteralNode( + propertySignatures3 + ) + ); + } */ + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("RemoveOperationData"), + undefined, + factory.createIntersectionTypeNode(adNodes) + ) + ); + + // RemoveOperation + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("RemoveOperation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OakOperation"), + [ + factory.createLiteralTypeNode(factory.createStringLiteral("remove")), + factory.createTypeReferenceNode( + factory.createIdentifier("RemoveOperationData"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) + ] + ) + ) + ); + + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("Operation"), + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier("CreateOperation"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("UpdateOperation"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("RemoveOperation"), + undefined + ) + ]) + ) + ); +} + +const initialStatements = () => [ + // import { String, Text, Int, SpecificKey } from 'oak-domain/types/DataType'; + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports( + [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('String') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Int') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Uint') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Float') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Double') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Boolean') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Text') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Datetime') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('File') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Price') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Image') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('PrimaryKey') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('ForeignKey') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Geo') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('SingleGeo') + ) + ] + ) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}DataType`) + ), + + /* import { + Q_DateValue, Q_LogicKey, Q_NumberValue, FnCallKey, FnCallValue, + Q_StringValue, Q_FullTextKey, Q_FullTextValue, FnCallValueAs, + Q_BooleanValue, + } from 'oak-domain/types/Demand'; */ + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports( + [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Q_DateValue') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Q_BooleanValue') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Q_NumberValue') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Q_StringValue') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Q_EnumValue') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('NodeId') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('MakeFilter') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('FulltextFilter') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('ExprOp') + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('ExpressionKey') + ), + ] + ) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}Demand`) + ), + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("OneOf") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ValueOf") + ) + ]) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}Polyfill`) + ), + // import * as SubQuery from '../_SubQuery'; + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamespaceImport(factory.createIdentifier("SubQuery")) + ), + factory.createStringLiteral("../_SubQuery") + ), + // import { Filter as OakFilter } from 'oak-domain/src/types/Entity'; + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("FormCreateData") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("FormUpdateData") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("DeduceAggregation") + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("Operation"), + factory.createIdentifier("OakOperation") + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("Selection"), + factory.createIdentifier("OakSelection") + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("MakeAction"), + factory.createIdentifier("OakMakeAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("EntityShape") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("AggregationResult") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("OtmKey") + ), + ]) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}Entity`), + undefined + ) +]; + +function outputSubQuery(outputDir: string, printer: ts.Printer) { + const statements: ts.Statement[] = []; + if (process.env.COMPLING_AS_LIB) { + } + for (const entity in Schema) { + // import * as User from '../User/Schema'; + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamespaceImport(factory.createIdentifier(entity)) + ), + factory.createStringLiteral(`./${entity}/Schema`) + ) + ); + } + const entities = keys(Schema); + + // 每个有manyToOne的Entity都会输出${One}IdSubQuery + for (const one in Schema) { + const identifier = `${one}IdSubQuery`; + const fromEntites = OneToMany[one] ? uniq(OneToMany[one] + .filter( + ([e, f]) => f !== 'entity' + ).map( + ([e]) => e + )) : []; + fromEntites.push(one); + + const inUnionTypeNode: ts.TypeNode[] = fromEntites.map( + ele => factory.createIntersectionTypeNode( + [ + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier(ele), + factory.createIdentifier(identifier) + ), + undefined + ), + factory.createTypeLiteralNode([factory.createPropertySignature( + undefined, + factory.createIdentifier("entity"), + undefined, + factory.createLiteralTypeNode(factory.createStringLiteral(firstLetterLowerCase(ele))) + )]) + ] + ) + ); + + if (process.env.COMPLING_AS_LIB) { + // 如果是建立 base,这里要加上额外可能的对象信息 + inUnionTypeNode.push( + factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ); + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier(identifier), + undefined, + factory.createMappedTypeNode( + undefined, + factory.createTypeParameterDeclaration( + undefined, + factory.createIdentifier("K"), + factory.createUnionTypeNode([ + factory.createLiteralTypeNode(factory.createStringLiteral("$in")), + factory.createLiteralTypeNode(factory.createStringLiteral("$nin")) + ]), + undefined + ), + undefined, + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createUnionTypeNode( + inUnionTypeNode + ), + undefined + ) + + ) + ); + } + + const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS); + const result = printer.printNode( + ts.EmitHint.Unspecified, + factory.createSourceFile(statements, factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None), + resultFile + ); + + const fileName = PathLib.join(outputDir, '_SubQuery.ts'); + writeFileSync(fileName, result, { flag: 'w' }); +} + +function outputEntityDict(outputDir: string, printer: ts.Printer) { + const statements: ts.Statement[] = []; + const propertySignatures: ts.PropertySignature[] = []; + for (const entity in Schema) { + // import * as User from '../User/Schema'; + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + factory.createIdentifier("EntityDef"), + factory.createIdentifier(entity) + )]) + ), + factory.createStringLiteral(`./${entity}/Schema`) + ) + ); + + const entityLc = firstLetterLowerCase(entity); + propertySignatures.push( + factory.createPropertySignature( + undefined, + factory.createIdentifier(entityLc), + undefined, + factory.createTypeReferenceNode(entity) + ) + ); + } + + if (/* process.env.COMPLING_AS_LIB */false) { + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("EntityDef") + )]) + ), + factory.createStringLiteral("../types/Entity"), + undefined + ), + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("EntityDict"), + undefined, + factory.createIntersectionTypeNode([ + factory.createTypeLiteralNode( + propertySignatures + ), + factory.createTypeLiteralNode([ + factory.createIndexSignature( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier("E"), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined + )], + factory.createTypeReferenceNode( + factory.createIdentifier("EntityDef"), + undefined + ) + ) + ]) + ]) + ) + ); + } + else { + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("EntityDict"), + undefined, + factory.createTypeLiteralNode( + propertySignatures + ) + ) + ); + } + + const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS); + const result = printer.printNode( + ts.EmitHint.Unspecified, + factory.createSourceFile(statements, factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None), + resultFile + ); + + const fileName = PathLib.join(outputDir, 'EntityDict.ts'); + writeFileSync(fileName, result, { flag: 'w' }); +} + +function outputSchema(outputDir: string, printer: ts.Printer) { + for (const entity in Schema) { + const statements: ts.Statement[] = initialStatements(); + if (ActionAsts[entity]) { + const { importedFrom, actionDefNames } = ActionAsts[entity]; + const localActions: string[] = ['Action', 'ParticularAction']; + for (const a in importedFrom) { + assert(a.endsWith('Action')); + const s = a.slice(0, a.length - 6).concat('State'); + if (importedFrom[a] === 'local' && actionDefNames.includes(firstLetterLowerCase(a.slice(0, a.length - 6)))) { + localActions.push(s); + } + else if (actionDefNames.includes(firstLetterLowerCase(a.slice(0, a.length - 6)))) { + const { moduleSpecifier } = importedFrom[a] as ts.ImportDeclaration; + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports( + [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(s) + ) + ] + ) + ), + moduleSpecifier, + undefined + ) + ); + } + } + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports(localActions.map( + ele => factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier(ele) + ) + )) + ), + factory.createStringLiteral('./Action'), + undefined + ), + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("RelationAction") + ), + ]) + ), + factory.createStringLiteral(ACTION_CONSTANT_IN_OAK_DOMAIN()), + undefined + ) + ); + } + else { + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("GenericAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("AppendOnlyAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ReadOnlyAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ExcludeUpdateAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ExcludeRemoveAction") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("RelationAction") + ), + ]) + ), + factory.createStringLiteral(ACTION_CONSTANT_IN_OAK_DOMAIN()), + undefined + ) + ); + } + const { additionalImports } = Schema[entity]; + if (additionalImports?.length > 0) { + statements.push(...additionalImports); + } + + // Relation定义加入 + if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef as ts.Node)) { + const node = Schema[entity].hasRelationDef as ts.TypeAliasDeclaration; + statements.push( + factory.updateTypeAliasDeclaration( + node, + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + node.name, + node.typeParameters, + node.type + ) + ); + } + + constructSchema(statements, entity); + constructFilter(statements, entity); + constructProjection(statements, entity); + constructSorter(statements, entity); + constructActions(statements, entity); + constructQuery(statements, entity); + // 现在FullAttrs和NativeAttrs似乎没什么用,还会引起递归 + // constructFullAttrs(statements, entity); + + const makeActionArguments: ts.TypeNode[] = []; + if (ActionAsts[entity]) { + makeActionArguments.push(factory.createTypeReferenceNode('Action')); + } + else { + makeActionArguments.push( + factory.createTypeReferenceNode( + OriginActionDict[Schema[entity].actionType as keyof typeof OriginActionDict], + ) + ); + } + if (Schema[entity].hasRelationDef || entity === 'User') { + makeActionArguments.push( + factory.createTypeReferenceNode('RelationAction') + ); + } + const actionTypeNode: ts.TypeNode = factory.createTypeReferenceNode( + factory.createIdentifier('OakMakeAction'), + makeActionArguments.length === 1 ? makeActionArguments : [factory.createUnionTypeNode(makeActionArguments)] + ); + const EntityDefAttrs = [ + factory.createPropertySignature( + undefined, + factory.createIdentifier("Schema"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Schema"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("OpSchema"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Action"), + undefined, + process.env.COMPLING_AS_LIB ? + factory.createUnionTypeNode( + [ + actionTypeNode, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ] + ) : actionTypeNode + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Selection"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Selection"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Aggregation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Aggregation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Operation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Operation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Create"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("CreateOperation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Update"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("UpdateOperation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("Remove"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("RemoveOperation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("CreateSingle"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("CreateSingleOperation"), + undefined + ) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("CreateMulti"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("CreateMultipleOperation"), + undefined + ) + ), + ]; + if (ActionAsts[entity]) { + EntityDefAttrs.push( + factory.createPropertySignature( + undefined, + factory.createIdentifier("ParticularAction"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('ParticularAction'), + undefined + ) + ) + ); + } + if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef as ts.Node)) { + EntityDefAttrs.push( + factory.createPropertySignature( + undefined, + factory.createIdentifier("Relation"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier('Relation'), + undefined + ) + ) + ); + } + statements.push( + factory.createTypeAliasDeclaration( + undefined, + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createIdentifier("EntityDef"), + undefined, + factory.createTypeLiteralNode(EntityDefAttrs) + ) + ) + + const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), Schema[entity].sourceFile); + const fileName = PathLib.join(outputDir, entity, 'Schema.ts'); + writeFileSync(fileName, result, { flag: 'w' }); + } +} + +function outputAction(outputDir: string, printer: ts.Printer) { + const actionDictStatements: ts.Statement[] = []; + const propertyAssignments: ts.PropertyAssignment[] = []; + for (const entity in ActionAsts) { + const { sourceFile, statements, importedFrom, actionDefNames } = ActionAsts[entity]; + const importStatements: ts.Statement[] = []; + for (const k in importedFrom) { + assert(k.endsWith('Action')); + if (importedFrom[k] !== 'local') { + importStatements.push( + importedFrom[k] as ts.ImportDeclaration + ); + } + } + /* const actionDiff = difference(actionNames, actionDefNames); + if (actionDiff.length > 0) { + throw new Error(`action not conform to actionDef: ${actionDiff.join(',')}, entity: ${entity}`); + } */ + statements.push( + factory.createVariableStatement( + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("ActionDefDict"), + undefined, + undefined, + factory.createObjectLiteralExpression( + actionDefNames.map( + ele => factory.createPropertyAssignment( + factory.createIdentifier(`${ele}State`), + factory.createIdentifier(`${firstLetterUpperCase(ele)}ActionDef`) + ) + ), + true + ) + )], + ts.NodeFlags.Const + ) + ) + ); + /* const result = printer.printNode( + ts.EmitHint.Unspecified, + factory.createSourceFile(statements, + factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None), + sourceFile + ); */ + // 这里如果用printNode,stringLiteral的输出始终有个bug不知道如何处理 + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(importStatements.concat(statements)), + sourceFile); + const filename = PathLib.join(outputDir, entity, 'Action.ts'); + writeFileSync(filename, result, { flag: 'w' }); + + actionDictStatements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + factory.createIdentifier("ActionDefDict"), + factory.createIdentifier(entity) + )]) + ), + factory.createStringLiteral(`./${entity}/Action`) + ) + ); + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier(firstLetterLowerCase(entity)), + factory.createIdentifier(entity) + ) + ); + } + + actionDictStatements.push( + factory.createVariableStatement( + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("ActionDefDict"), + undefined, + undefined, + factory.createObjectLiteralExpression( + propertyAssignments, + true + ) + )], + ts.NodeFlags.Const + ) + ) + ); + + const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS); + const result = printer.printNode( + ts.EmitHint.Unspecified, + factory.createSourceFile(actionDictStatements, factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None), + resultFile + ); + + const fileName = PathLib.join(outputDir, 'ActionDefDict.ts'); + writeFileSync(fileName, result, { flag: 'w' }); +} + +function constructAttributes(entity: string): ts.PropertyAssignment[] { + const { schemaAttrs, enumAttributes } = Schema[entity]; + const { [entity]: manyToOneSet } = ManyToOne; + const result: ts.PropertyAssignment[] = []; + + schemaAttrs.forEach( + (attr) => { + const attrAssignments: ts.PropertyAssignment[] = []; + const { name, type, questionToken: allowNull } = attr; + if (!allowNull) { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("notNull"), + factory.createTrue(), + ), + ); + } + let name2 = name; + + if (ts.isTypeReferenceNode(type!)) { + const { typeName, typeArguments } = type; + if (ts.isIdentifier(typeName)) { + const { text } = typeName; + switch (text) { + case 'String': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("varchar") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("length"), + factory.createNumericLiteral( + ((typeArguments![0]).literal).text + ) + ), + ], + true + ) + ) + ); + // 如果是entity,在这里处理一下ref + if (ts.isIdentifier(name) && name.text === 'entity') { + const mtoRelations = ReversePointerRelations[entity]; + if (mtoRelations) { + const mtoEntities = mtoRelations.map( + ele => firstLetterLowerCase(ele) + ); + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("ref"), + factory.createArrayLiteralExpression( + mtoEntities.map( + ele => factory.createStringLiteral(ele) + ), + false + ) + ) + ); + } + } + break; + } + case 'Text': + case 'Image': + case 'File': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("text") + ), + ); + break; + } + case 'Int': + case 'Uint': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("int") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("width"), + factory.createNumericLiteral( + ((typeArguments![0]).literal).text + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("signed"), + text === 'Uint' ? factory.createFalse() : factory.createTrue() + ) + ], + true + ) + ) + ); + break; + } + case 'Double': + case 'Float': + case 'Decimal': { + if (['Double', 'Float'].includes(text)) { + console.warn(`${entity}对象中还有${text}类型定义,现在统一用Decimal进行存储`); + } + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("decimal") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("precision"), + factory.createNumericLiteral( + ((typeArguments![0]).literal).text + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("scale"), + factory.createNumericLiteral( + ((typeArguments![1]).literal).text + ) + ) + ], + true + ) + ) + ); + break; + } + case 'Boolean': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("boolean") + ) + ); + break; + } + case 'Price': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("money") + ), + ); + break; + } + case 'Datetime': { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("datetime") + ), + ); + break; + } + case 'SingleGeo': + case 'Geo': { + // object类型暂不支持查询 + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("geometry") + ), + ); + break; + } + case 'Object': { + // object类型暂不支持查询 + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("object") + ), + ); + break; + } + default: { + const text2 = text === 'Schema' ? entity : text; + const manyToOneItem = manyToOneSet && manyToOneSet.find( + ([refEntity, attrName]) => refEntity === text2 && attrName === attrName + ); + if (manyToOneItem) { + // 外键 + name2 = factory.createIdentifier(`${(name).text}Id`); + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("ref") + ), + factory.createPropertyAssignment( + factory.createIdentifier("ref"), + factory.createStringLiteral(firstLetterLowerCase(text2)) + ) + ); + } + else { + if (enumAttributes && enumAttributes[(name).text]) { + attrAssignments.push( + factory.createPropertyAssignment( + 'type', + factory.createStringLiteral("enum") + ), + factory.createPropertyAssignment( + 'enumeration', + factory.createArrayLiteralExpression( + enumAttributes[(name).text].map( + ele => factory.createStringLiteral(ele) + ) + ) + ) + ); + } + else { + // todo 引用的非string定义,目前没有处理int类型的引用,等遇到了再处理 + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("object") + ), + ); + } + } + } + } + } + else { + assert(false); + } + } + else { + if (ts.isUnionTypeNode(type!)) { + if (ts.isLiteralTypeNode(type.types[0])) { + if (ts.isStringLiteral(type.types[0].literal)) { + assert(enumAttributes && enumAttributes[(name).text]); + attrAssignments.push( + factory.createPropertyAssignment( + 'type', + factory.createStringLiteral("enum") + ), + factory.createPropertyAssignment( + 'enumeration', + factory.createArrayLiteralExpression( + enumAttributes[(name).text].map( + ele => factory.createStringLiteral(ele) + ) + ) + ) + ); + } + else { + assert(ts.isNumericLiteral(type.types[0].literal)); + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("int") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("width"), + factory.createNumericLiteral(INT_LITERL_DEFAULT_WIDTH) + ) + ], + true + ) + ) + ); + } + } + else { + // 否则是本地规定的shape,直接用object + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("object") + ), + ); + } + } + else { + if (ts.isLiteralTypeNode(type!)) { + if (ts.isStringLiteral(type.literal)) { + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("varchar") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [factory.createPropertyAssignment( + factory.createIdentifier("length"), + factory.createNumericLiteral(STRING_LITERAL_MAX_LENGTH) + )], + true + ) + ) + ); + } + else { + assert(ts.isNumericLiteral(type.literal)); + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("precision") + ), + factory.createPropertyAssignment( + factory.createIdentifier("params"), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("precision"), + factory.createNumericLiteral(NUMERICAL_LITERL_DEFAULT_PRECISION) + ), + factory.createPropertyAssignment( + factory.createIdentifier("scale"), + factory.createNumericLiteral(NUMERICAL_LITERL_DEFAULT_SCALE) + ) + ], + true + ) + ) + ); + } + } + else { + // 否则是本地规定的shape,直接用object + attrAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("type"), + factory.createStringLiteral("object") + ), + ); + } + } + } + + result.push( + factory.createPropertyAssignment( + name2, + factory.createObjectLiteralExpression(attrAssignments, true) + ) + ); + } + ); + + return result; +} + +function outputLocale(outputDir: string, printer: ts.Printer) { + const locales: Record = {}; + const entities: string[] = []; + for (const entity in Schema) { + const { locale, sourceFile } = Schema[entity]; + if (locale) { + const { properties } = locale; + properties.forEach( + (ele) => { + assert(ts.isPropertyAssignment(ele) && (ts.isIdentifier(ele.name) || ts.isStringLiteral(ele.name)) && ts.isObjectLiteralExpression(ele.initializer)); + const lng = ele.name.text; + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray([ + factory.createReturnStatement( + ele.initializer + ) + ]), + sourceFile); + const data = Function(result)(); + const filename = PathLib.join(outputDir, entity, 'locales', `${lng}.json`); + writeFileSync(filename, JSON.stringify(data), { flag: 'w' }); + + if (locales[lng]) { + locales[lng].push(entity); + } + else { + locales[lng] = [entity]; + } + } + ); + entities.push(entity); + } + } + + for (const lng in locales) { + if (locales[lng].length < entities.length) { + const lack = difference(entities, locales[lng]); + throw new Error(`${lng}语言定义中缺少了对象${lack.join(',')}的定义,请检查相应的定义文件`); + } + /* const statements: ts.Statement[] = locales[lng].map( + (entity) => factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + factory.createIdentifier(firstLetterLowerCase(entity)), + undefined + ), + factory.createStringLiteral(`../${entity}/locales/${lng}`), + undefined + ) + ); + + statements.push( + factory.createExportAssignment( + undefined, + undefined, + undefined, + factory.createObjectLiteralExpression( + locales[lng].map( + ele => factory.createShorthandPropertyAssignment( + factory.createIdentifier(firstLetterLowerCase(ele)), + undefined + ) + ), + true + ) + ) + ); + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(statements), + ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS)); + const filename = path.join(outputDir, '_locales', `${lng}.ts`); + writeFileSync(filename, result, { flag: 'w' }); */ + } +} + +function outputStorage(outputDir: string, printer: ts.Printer) { + const importStatements: ts.Statement[] = [ + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("StorageSchema") + )]) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN(1)}Storage`), + undefined + ), + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("EntityDict") + )]) + ), + factory.createStringLiteral("./EntityDict"), + undefined + ) + ]; + const entityAssignments: ts.PropertyAssignment[] = []; + + for (const entity in Schema) { + const indexExpressions: ts.Expression[] = []; + const { sourceFile, inModi, indexes, toModi, actionType, static: _static, hasRelationDef } = Schema[entity]; + const fromSchemaSpecifiers = [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("OpSchema") + ) + ]; + /* if (relationHierarchy || reverseCascadeRelationHierarchy) { + fromSchemaSpecifiers.push( + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("Relation") + ) + ); + } */ + const statements: ts.Statement[] = [ + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("StorageDesc") + )]) + ), + factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN()}Storage`), + undefined + ), + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports(fromSchemaSpecifiers) + ), + factory.createStringLiteral("./Schema"), + undefined + ) + ]; + + const needImportActions: ts.ImportSpecifier[] = []; + switch (actionType) { + case 'readOnly': { + needImportActions.push( + factory.createImportSpecifier( + false, + factory.createIdentifier("readOnlyActions"), + factory.createIdentifier("actions") + ) + ); + break; + } + case 'appendOnly': { + needImportActions.push( + factory.createImportSpecifier( + false, + factory.createIdentifier("appendOnlyActions"), + factory.createIdentifier("actions") + ) + ); + break; + } + case 'excludeUpdate': { + needImportActions.push( + factory.createImportSpecifier( + false, + factory.createIdentifier("excludeUpdateActions"), + factory.createIdentifier("actions") + ) + ); + break; + } + default: { + if (ActionAsts[entity]) { + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("actions") + )]) + ), + factory.createStringLiteral("./Action"), + undefined + ) + ); + } + else { + needImportActions.push( + factory.createImportSpecifier( + false, + factory.createIdentifier("genericActions"), + factory.createIdentifier("actions") + ) + ); + } + } + } + + if (Schema[entity].hasRelationDef || entity === 'User') { + needImportActions.push( + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("relationActions"), + ) + ); + } + if (needImportActions.length > 0) { + statements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports(needImportActions) + ), + factory.createStringLiteral(ACTION_CONSTANT_IN_OAK_DOMAIN()), + undefined + ) + ); + } + const propertyAssignments: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = []; + const attributes = constructAttributes(entity); + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("attributes"), + factory.createObjectLiteralExpression( + attributes, + true + ) + ) + ); + + if (indexes) { + indexExpressions.push( + ...indexes.elements + ) + } + if (toModi) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("toModi"), + factory.createTrue() + ) + ); + } + + if (inModi) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("inModi"), + factory.createTrue() + ) + ); + } + + if (_static || actionType === 'readOnly') { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("static"), + factory.createTrue() + ) + ); + } + + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("actionType"), + factory.createStringLiteral(actionType) + ) + ); + + if (Schema[entity].hasRelationDef || entity === 'User') { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("actions"), + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("actions"), + factory.createIdentifier("concat") + ), + undefined, + [factory.createIdentifier("relationActions")] + ) + ) + ); + } + else { + propertyAssignments.push( + factory.createShorthandPropertyAssignment( + factory.createIdentifier("actions"), + undefined + ) + ); + } + + if (indexExpressions.length > 0) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("indexes"), + factory.createArrayLiteralExpression(indexExpressions, true) + ) + ); + } + /* if (relationHierarchy) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("relationHierarchy"), + relationHierarchy, + ) + ); + } + if (reverseCascadeRelationHierarchy) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("reverseCascadeRelationHierarchy"), + reverseCascadeRelationHierarchy, + ) + ); + } */ + if (hasRelationDef) { + const { type } = hasRelationDef; + if (ts.isUnionTypeNode(type)) { + const { types } = type; + const relationTexts = types.map( + ele => { + assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal)); + return ele.literal.text; + } + ) + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("relation"), + factory.createArrayLiteralExpression(relationTexts.map( + ele => factory.createStringLiteral(ele) + )), + ) + ); + } + else { + assert(ts.isLiteralTypeNode(type)); + assert(ts.isStringLiteral(type.literal)); + + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("relation"), + factory.createArrayLiteralExpression( + [ + type.literal + ] + ), + ) + ); + } + } + const sdTypeArguments = [ + factory.createTypeReferenceNode( + factory.createIdentifier("OpSchema"), + undefined + ) + ]; + statements.push( + factory.createVariableStatement( + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("desc"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("StorageDesc"), + sdTypeArguments + ), + factory.createObjectLiteralExpression( + propertyAssignments, + true + ) + )], + ts.NodeFlags.Const + ) + ) + ); + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(statements), + sourceFile); + const filename = PathLib.join(outputDir, entity, 'Storage.ts'); + writeFileSync(filename, result, { flag: 'w' }); + + importStatements.push( + factory.createImportDeclaration( + undefined, + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + factory.createIdentifier("desc"), + factory.createIdentifier(`${firstLetterLowerCase(entity)}Desc`) + ) + ]) + ), + factory.createStringLiteral(`./${entity}/Storage`), + undefined + ) + ); + entityAssignments.push( + factory.createPropertyAssignment( + firstLetterLowerCase(entity), + factory.createIdentifier(`${firstLetterLowerCase(entity)}Desc`) + ) + ); + } + + importStatements.push( + factory.createVariableStatement( + [factory.createModifier(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("storageSchema"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("StorageSchema"), + [ + factory.createTypeReferenceNode('EntityDict') + ] + ), + factory.createObjectLiteralExpression( + entityAssignments, + true + ) + )], + ts.NodeFlags.Const + ) + ) + ); + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(importStatements), + ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS)); + const filename = PathLib.join(outputDir, 'Storage.ts'); + writeFileSync(filename, result, { flag: 'w' }); +} + +function resetOutputDir(outputDir: string) { + emptydirSync(outputDir); + + for (const moduleName in Schema) { + mkdirSync(PathLib.join(outputDir, moduleName)); + mkdirSync(PathLib.join(outputDir, moduleName, 'locales')); + } + mkdirSync(PathLib.join(outputDir, '_locales')) +} + +function addReverseRelationship() { + for (const reverseEntity in ReversePointerRelations) { + if (!ReversePointerEntities.hasOwnProperty(reverseEntity)) { + throw new Error(`「${reverseEntity}」被引用为一个反指对象,但其定义中的entity和entityId不符合要求`); + } + for (const one of ReversePointerRelations[reverseEntity]) { + addRelationship(reverseEntity, one, 'entity', false); + } + } +} + +function outputIndexTs(outputDir: string) { + const indexTs = `export * from './EntityDict'; + export * from './Storage'; + export * from './ActionDefDict'; + `; + const filename = PathLib.join(outputDir, 'index.ts'); + writeFileSync(filename, indexTs, { flag: 'w' }); +} + +function outputPackageJson(outputDir: string) { + const pj = { + "name": process.env.COMPLING_AS_LIB ? "general-app-domain" : "oak-app-domain", + "main": "index.ts" + }; + + const filename = PathLib.join(outputDir, 'package.json'); + writeFileSync(filename, JSON.stringify(pj), { flag: 'w' }); +} + +/** + * (从toModi的对象开始)分析可能被modi指向的对象 + */ +function analyzeInModi() { + const getRelateEntities = (entity: string) => { + let result: string[] = []; + if (ManyToOne[entity]) { + // 用反指指针指向的对象可以忽略,因为前端不可能设计出这样的更新页面 + result = ManyToOne[entity].filter( + ele => ele[1] !== 'entity' + ).map( + ele => ele[0] + ); + } + + if (OneToMany[entity]) { + result.push( + ...OneToMany[entity].map( + ele => ele[0] + ) + ); + } + return uniq(result); + }; + const setInModi = (entity: string) => { + if (['Modi', 'ModiEntity', 'Oper', 'OperEntity', 'User'].includes(entity)) { + return; + } + const schema = Schema[entity]; + if (schema.toModi || schema.inModi || schema.actionType === 'readOnly' || schema.static) { + return; + } + schema.inModi = true; + const related = getRelateEntities(entity); + related.forEach( + ele => setInModi(ele) + ); + }; + for (const entity in Schema) { + if (Schema[entity].toModi) { + const related = getRelateEntities(entity); + related.forEach( + ele => setInModi(ele) + ); + } + } +} + +export function analyzeEntities(inputDir: string, relativePath?: string) { + const files = readdirSync(inputDir); + const fullFilenames = files.map( + ele => { + const entity = ele.slice(0, ele.indexOf('.')) + if (RESERVED_ENTITIES.includes(entity) || RESERVED_ENTITIES.find( + ele2 => entity.startsWith(ele2) + )) { + throw new Error(`${ele}是系统保留字,请勿使用其当对象名或对象名前缀`); + } + return `${inputDir}/${ele}`; + } + ); + + const program = ts.createProgram(fullFilenames, { allowJs: true }); + + files.forEach( + (filename) => { + analyzeEntity(filename, inputDir, program, relativePath); + } + ); + analyzeInModi(); + uniqRelationships(); +} + +export function buildSchema(outputDir: string): void { + addReverseRelationship(); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + resetOutputDir(outputDir); + outputSchema(outputDir, printer); + outputLocale(outputDir, printer); + outputSubQuery(outputDir, printer); + outputAction(outputDir, printer); + outputEntityDict(outputDir, printer); + outputStorage(outputDir, printer); + outputIndexTs(outputDir); + + if (!process.env.COMPLING_AS_LIB) { + outputPackageJson(outputDir); + } +} From f6deee33026cb467b29d1f71d3dda484a7a03a37 Mon Sep 17 00:00:00 2001 From: wenjiarui Date: Wed, 31 May 2023 13:46:44 +0800 Subject: [PATCH 10/14] =?UTF-8?q?auth=E7=9A=84checker=E6=9D=83=E9=99=90?= =?UTF-8?q?=E4=B8=8D=E8=B6=B3=E6=97=B6=E4=B8=8D=E7=9B=B4=E6=8E=A5=E6=8A=9B?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=EF=BC=8C=E5=BE=85checker=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=AE=8C=E5=90=8E=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/checker.js | 24 +++++++++++++++++------- src/store/checker.ts | 26 ++++++++++++++++++-------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/store/checker.js b/lib/store/checker.js index 716d57b..cbaca3d 100644 --- a/lib/store/checker.js +++ b/lib/store/checker.js @@ -441,19 +441,20 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) { if (d.entity === attr && typeof d.entityId === 'string') { return d.entityId; } - throw new Exception_1.OakUserUnpermittedException(); } else { (0, assert_1.default)(typeof relation === 'string'); if (typeof d["".concat(attr, "Id")] === 'string') { return d["".concat(attr, "Id")]; } - throw new Exception_1.OakUserUnpermittedException(); } }; if (relation === 2) { if (data instanceof Array) { - var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); })); + var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }).filter(function (ele) { return !!ele; })); + if (fkIds.length === 0) { + return new Exception_1.OakUserUnpermittedException(); + } return { $entity: attr, $filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }), @@ -461,6 +462,9 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) { }; } var fkId_1 = getForeignKeyId_1(data); + if (!fkId_1) { + return new Exception_1.OakUserUnpermittedException(); + } return { $entity: attr, $filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId_1 }), @@ -468,7 +472,10 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) { } (0, assert_1.default)(typeof relation === 'string'); if (data instanceof Array) { - var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); })); + var fkIds = (0, lodash_1.uniq)(data.map(function (d) { return getForeignKeyId_1(d); }).filter(function (ele) { return !!ele; })); + if (fkIds.length === 0) { + return new Exception_1.OakUserUnpermittedException(); + } return { $entity: relation, $filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: { $in: fkIds } }), @@ -476,6 +483,9 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) { }; } var fkId = getForeignKeyId_1(data); + if (!fkId) { + return new Exception_1.OakUserUnpermittedException(); + } return { $entity: relation, $filter: (0, filter_1.addFilterSegment)(filterMaker2(userId), { id: fkId }), @@ -486,9 +496,9 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) { var filter_4 = operation.filter; if (filter_4) { var counter = translateCreateFilterMaker(entity2, filter_4, userId); - if (counter instanceof Exception_1.OakUserUnpermittedException) { - throw counter; - } + // if (counter instanceof OakUserUnpermittedException) { + // throw counter; + // } return counter; } throw new Exception_1.OakUserUnpermittedException(); diff --git a/src/store/checker.ts b/src/store/checker.ts index fda57fa..d69c717 100644 --- a/src/store/checker.ts +++ b/src/store/checker.ts @@ -245,7 +245,7 @@ type CreateRelationCounter = CreateRelat type FilterMakeFn = - (operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'], userId: string) => ED[keyof ED]['Selection']['filter'] | CreateRelationCounter; + (operation: ED[keyof ED]['Operation'] | ED[keyof ED]['Selection'], userId: string) => ED[keyof ED]['Selection']['filter'] | CreateRelationCounter | OakUserUnpermittedException; function translateCascadeRelationFilterMaker( schema: StorageSchema, @@ -442,19 +442,20 @@ function translateCascadeRelationFilterMaker getForeignKeyId(d))); + const fkIds = uniq(data.map(d => getForeignKeyId(d)).filter(ele => !!ele)); + if (fkIds.length === 0) { + return new OakUserUnpermittedException(); + } return { $entity: attr, $filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }), @@ -462,6 +463,9 @@ function translateCascadeRelationFilterMaker getForeignKeyId(d))); + const fkIds = uniq(data.map(d => getForeignKeyId(d)).filter(ele => !!ele)); + if (fkIds.length === 0) { + return new OakUserUnpermittedException(); + } return { $entity: relation, $filter: addFilterSegment(filterMaker2(userId), { id: { $in: fkIds } }), @@ -477,6 +484,9 @@ function translateCascadeRelationFilterMaker Date: Fri, 9 Jun 2023 17:44:01 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86bridge?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E5=A4=96=E9=83=A8=E8=B5=84=E6=BA=90=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/types/Connector.d.ts | 6 ++++++ lib/utils/SimpleConnector.d.ts | 12 +++++++++++ lib/utils/SimpleConnector.js | 32 +++++++++++++++++++++++++++++ src/types/Connector.ts | 9 +++++++++ src/utils/SimpleConnector.ts | 37 ++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/lib/types/Connector.d.ts b/lib/types/Connector.d.ts index 1606e5f..2b38523 100644 --- a/lib/types/Connector.d.ts +++ b/lib/types/Connector.d.ts @@ -24,4 +24,10 @@ export declare abstract class Connector; }; + abstract getBridgeRouter(): string; + abstract makeBridgeUrl(url: string, headers?: Record): string; + abstract parseBridgeRequestQuery(urlParams: string): { + url: string; + headers?: Record; + }; } diff --git a/lib/utils/SimpleConnector.d.ts b/lib/utils/SimpleConnector.d.ts index 197a2be..173f098 100644 --- a/lib/utils/SimpleConnector.d.ts +++ b/lib/utils/SimpleConnector.d.ts @@ -5,6 +5,7 @@ import { SyncContext } from '../store/SyncRowStore'; import { Connector, EntityDict, OakException } from "../types"; export declare class SimpleConnector, FrontCxt extends SyncContext> extends Connector { static ROUTER: string; + static BRIDGE_ROUTER: string; private serverUrl; private makeException; private contextBuilder; @@ -32,4 +33,15 @@ export declare class SimpleConnector | undefined; }; + getBridgeRouter(): string; + /** + * 通过桥接访问外部资源 + * @param url + * @param headers + */ + makeBridgeUrl(url: string, headers?: Record): string; + parseBridgeRequestQuery(urlParams: string): { + url: string; + headers?: Record | undefined; + }; } diff --git a/lib/utils/SimpleConnector.js b/lib/utils/SimpleConnector.js index b26434e..cba0f83 100644 --- a/lib/utils/SimpleConnector.js +++ b/lib/utils/SimpleConnector.js @@ -4,6 +4,7 @@ exports.SimpleConnector = void 0; var tslib_1 = require("tslib"); var assert_1 = tslib_1.__importDefault(require("assert")); var stream_1 = require("stream"); +var url_1 = tslib_1.__importDefault(require("url")); var types_1 = require("../types"); function makeContentTypeAndBody(data) { // @@ -139,7 +140,38 @@ var SimpleConnector = /** @class */ (function (_super) { }, }; }; + SimpleConnector.prototype.getBridgeRouter = function () { + return SimpleConnector.BRIDGE_ROUTER; + }; + /** + * 通过桥接访问外部资源 + * @param url + * @param headers + */ + SimpleConnector.prototype.makeBridgeUrl = function (url, headers) { + if (process.env.NODE_ENV === 'development' && process.env.PROD !== 'true') { + console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); + return url; + } + var search = new url_1.default.URLSearchParams({ + url: url, + }); + if (headers) { + search.append('headers', JSON.stringify(headers)); + } + return "".concat(this.getBridgeRouter(), "?").concat(search.toString()); + }; + SimpleConnector.prototype.parseBridgeRequestQuery = function (urlParams) { + var search = new url_1.default.URLSearchParams(urlParams); + var url = search.get('url'); + var headers = search.get('headers'); + return { + url: url, + headers: headers && JSON.parse(headers), + }; + }; SimpleConnector.ROUTER = '/aspect'; + SimpleConnector.BRIDGE_ROUTER = '/bridge'; return SimpleConnector; }(types_1.Connector)); exports.SimpleConnector = SimpleConnector; diff --git a/src/types/Connector.ts b/src/types/Connector.ts index eb3e15b..87ec26c 100644 --- a/src/types/Connector.ts +++ b/src/types/Connector.ts @@ -25,4 +25,13 @@ export abstract class Connector; }; + + abstract getBridgeRouter(): string; + + abstract makeBridgeUrl(url: string, headers?: Record): string; + + abstract parseBridgeRequestQuery(urlParams: string): { + url: string; + headers?: Record; + } } \ No newline at end of file diff --git a/src/utils/SimpleConnector.ts b/src/utils/SimpleConnector.ts index d73e99b..3f0c988 100644 --- a/src/utils/SimpleConnector.ts +++ b/src/utils/SimpleConnector.ts @@ -1,6 +1,7 @@ import assert from 'assert'; import { IncomingHttpHeaders } from "http"; import { Stream } from 'stream'; +import URL from 'url'; import { AsyncContext, AsyncRowStore } from '../store/AsyncRowStore'; import { SyncContext } from '../store/SyncRowStore'; import { Connector, EntityDict, OakException, OakExternalException, OpRecord } from "../types"; @@ -24,6 +25,7 @@ function makeContentTypeAndBody(data: any) { export class SimpleConnector, FrontCxt extends SyncContext> extends Connector { static ROUTER = '/aspect'; + static BRIDGE_ROUTER = '/bridge'; private serverUrl: string; private makeException: (exceptionData: any) => OakException; private contextBuilder: (str: string | undefined) => (store: AsyncRowStore) => Promise; @@ -130,4 +132,39 @@ export class SimpleConnector) { + if (process.env.NODE_ENV === 'development' && process.env.PROD !== 'true') { + console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); + return url; + } + + const search = new URL.URLSearchParams({ + url, + }); + if (headers) { + search.append('headers', JSON.stringify(headers)); + } + + return `${this.getBridgeRouter()}?${search.toString()}`; + } + + parseBridgeRequestQuery(urlParams: string): { url: string; headers?: Record | undefined; } { + const search = new URL.URLSearchParams(urlParams); + const url = search.get('url') as string; + const headers = search.get('headers'); + return { + url, + headers: headers && JSON.parse(headers), + }; + } } \ No newline at end of file From 75b1d3d131600fb9e137282456c9e4302e05142a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=9C=9D=E4=BC=9F?= <2211960668@qq.com> Date: Fri, 9 Jun 2023 20:49:20 +0800 Subject: [PATCH 12/14] =?UTF-8?q?makeBridgeUrl=E6=94=B9=E5=8A=A8=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/SimpleConnector.js | 24 +++++++++++++----------- src/utils/SimpleConnector.ts | 25 +++++++++++++------------ 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/utils/SimpleConnector.js b/lib/utils/SimpleConnector.js index cba0f83..dd72e2e 100644 --- a/lib/utils/SimpleConnector.js +++ b/lib/utils/SimpleConnector.js @@ -149,17 +149,19 @@ var SimpleConnector = /** @class */ (function (_super) { * @param headers */ SimpleConnector.prototype.makeBridgeUrl = function (url, headers) { - if (process.env.NODE_ENV === 'development' && process.env.PROD !== 'true') { - console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); - return url; - } - var search = new url_1.default.URLSearchParams({ - url: url, - }); - if (headers) { - search.append('headers', JSON.stringify(headers)); - } - return "".concat(this.getBridgeRouter(), "?").concat(search.toString()); + // if (process.env.PROD !== 'true') { + //     console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); + //     return url; + // } + var encodeUrl = encodeURIComponent(url); + // const urlParse = URL.parse(url, true); + // const { search } = urlParse as { + //     search: string; + // }; + // if (headers) { + //     search.append('headers', JSON.stringify(headers)); + // } + return "".concat(this.getBridgeRouter(), "?url=").concat(encodeUrl); }; SimpleConnector.prototype.parseBridgeRequestQuery = function (urlParams) { var search = new url_1.default.URLSearchParams(urlParams); diff --git a/src/utils/SimpleConnector.ts b/src/utils/SimpleConnector.ts index 3f0c988..b683934 100644 --- a/src/utils/SimpleConnector.ts +++ b/src/utils/SimpleConnector.ts @@ -143,19 +143,20 @@ export class SimpleConnector) { - if (process.env.NODE_ENV === 'development' && process.env.PROD !== 'true') { - console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); - return url; - } + // if (process.env.PROD !== 'true') { + //     console.warn('在development下无法通过bridge访问资源,将直接访问,可能失败', url); + //     return url; + // } + const encodeUrl = encodeURIComponent(url); + // const urlParse = URL.parse(url, true); + // const { search } = urlParse as { + //     search: string; + // }; + // if (headers) { + //     search.append('headers', JSON.stringify(headers)); + // } - const search = new URL.URLSearchParams({ - url, - }); - if (headers) { - search.append('headers', JSON.stringify(headers)); - } - - return `${this.getBridgeRouter()}?${search.toString()}`; + return `${this.getBridgeRouter()}?url=${encodeUrl}`; } parseBridgeRequestQuery(urlParams: string): { url: string; headers?: Record | undefined; } { From 6d85c2291a76585637d13213f49ab3606da4f219 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Mon, 12 Jun 2023 09:15:52 +0800 Subject: [PATCH 13/14] simpleConnector --- lib/utils/SimpleConnector.d.ts | 7 ++++--- lib/utils/SimpleConnector.js | 13 +++++++------ src/utils/SimpleConnector.ts | 23 ++++++++++++----------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/utils/SimpleConnector.d.ts b/lib/utils/SimpleConnector.d.ts index 173f098..c82b43d 100644 --- a/lib/utils/SimpleConnector.d.ts +++ b/lib/utils/SimpleConnector.d.ts @@ -4,9 +4,10 @@ import { AsyncContext, AsyncRowStore } from '../store/AsyncRowStore'; import { SyncContext } from '../store/SyncRowStore'; import { Connector, EntityDict, OakException } from "../types"; export declare class SimpleConnector, FrontCxt extends SyncContext> extends Connector { - static ROUTER: string; + static ASPECT_ROUTER: string; static BRIDGE_ROUTER: string; - private serverUrl; + private serverAspectUrl; + private serverBridgeUrl; private makeException; private contextBuilder; constructor(serverUrl: string, makeException: (exceptionData: any) => OakException, contextBuilder: (str: string | undefined) => (store: AsyncRowStore) => Promise); @@ -35,7 +36,7 @@ export declare class SimpleConnector, FrontCxt extends SyncContext> extends Connector { - static ROUTER = '/aspect'; + static ASPECT_ROUTER = '/aspect'; static BRIDGE_ROUTER = '/bridge'; - private serverUrl: string; + private serverAspectUrl: string; + private serverBridgeUrl: string; private makeException: (exceptionData: any) => OakException; private contextBuilder: (str: string | undefined) => (store: AsyncRowStore) => Promise; constructor(serverUrl: string, makeException: (exceptionData: any) => OakException, contextBuilder: (str: string | undefined) => (store: AsyncRowStore) => Promise) { super(); - this.serverUrl = `${serverUrl}${SimpleConnector.ROUTER}`; + this.serverAspectUrl = `${serverUrl}${SimpleConnector.ASPECT_ROUTER}`; + this.serverBridgeUrl = `${serverUrl}${SimpleConnector.BRIDGE_ROUTER}`; this.makeException = makeException; this.contextBuilder = contextBuilder; } @@ -41,7 +43,7 @@ export class SimpleConnector): Promise<{ name: string; params: any; context: BackCxt; }> { @@ -113,7 +115,7 @@ export class SimpleConnector | undefined; } { const search = new URL.URLSearchParams(urlParams); const url = search.get('url') as string; @@ -167,4 +168,4 @@ export class SimpleConnector Date: Mon, 12 Jun 2023 18:00:10 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E5=8D=83=E4=BD=8D=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E6=B3=95=E5=8A=A0=E9=80=97=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/money.d.ts | 3 ++- lib/utils/money.js | 18 +++++++++++++++++- src/utils/money.ts | 38 ++++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/utils/money.d.ts b/lib/utils/money.d.ts index 75a3307..09bc27a 100644 --- a/lib/utils/money.d.ts +++ b/lib/utils/money.d.ts @@ -2,4 +2,5 @@ declare const ToCent: (float: number) => number; declare const ToYuan: (int: number) => number; declare const StringToCent: (value: string, allowNegative?: true) => number | undefined; declare const CentToString: (value: number) => string | undefined; -export { ToCent, ToYuan, StringToCent, CentToString, }; +declare const ThousandCont: (value: number) => string | undefined; +export { ToCent, ToYuan, StringToCent, CentToString, ThousandCont }; diff --git a/lib/utils/money.js b/lib/utils/money.js index 0790b96..def3365 100644 --- a/lib/utils/money.js +++ b/lib/utils/money.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.CentToString = exports.StringToCent = exports.ToYuan = exports.ToCent = void 0; +exports.ThousandCont = exports.CentToString = exports.StringToCent = exports.ToYuan = exports.ToCent = void 0; var ToCent = function (float) { return Math.round(float * 100); }; @@ -22,3 +22,19 @@ var CentToString = function (value) { } }; exports.CentToString = CentToString; +var ThousandCont = function (value) { + var value1 = "".concat(value); + var numArr = value1.split('.'); + value1 = numArr[0]; + var result = ''; + while (value1.length > 3) { + result = ',' + value1.slice(-3) + result; + value1 = value1.slice(0, value1.length - 3); + } + if (value1) { + result = value1 + result; + } + result = result + '.' + numArr[1]; + return result; +}; +exports.ThousandCont = ThousandCont; diff --git a/src/utils/money.ts b/src/utils/money.ts index db77b10..ba80952 100644 --- a/src/utils/money.ts +++ b/src/utils/money.ts @@ -1,27 +1,41 @@ const ToCent: (float: number) => number = (float) => { return Math.round(float * 100); -} +}; -const ToYuan: (int: number) => number = ( int) => { +const ToYuan: (int: number) => number = (int) => { return Math.round(int) / 100; -} +}; -const StringToCent: (value: string, allowNegative?: true) => number | undefined = (value, allowNegative) => { +const StringToCent: ( + value: string, + allowNegative?: true +) => number | undefined = (value, allowNegative) => { const numValue = parseInt(value, 10); if (typeof numValue === 'number' && (numValue >= 0 || allowNegative)) { return ToCent(numValue); } -} +}; const CentToString: (value: number) => string | undefined = (value) => { if (typeof value === 'number') { return `${ToYuan(value)}`; } -} +}; -export { - ToCent, - ToYuan, - StringToCent, - CentToString, -} \ No newline at end of file +const ThousandCont: (value: number) => string | undefined = (value) => { + let value1 = `${value}`; + const numArr = value1.split('.'); + value1 = numArr[0]; + let result = ''; + while (value1.length > 3) { + result = ',' + value1.slice(-3) + result; + value1 = value1.slice(0, value1.length - 3); + } + if (value1) { + result = value1 + result; + } + result = result + '.' + numArr[1]; + return result; +}; + +export { ToCent, ToYuan, StringToCent, CentToString, ThousandCont };