diff --git a/lib/actions/action.d.ts b/lib/actions/action.d.ts index a2f31d3..7bec7e8 100644 --- a/lib/actions/action.d.ts +++ b/lib/actions/action.d.ts @@ -1,16 +1,16 @@ import { ActionDef } from '../types/Action'; -export type ReadOnlyAction = 'select' | 'count' | 'stat' | 'download'; -export type AppendOnlyAction = ReadOnlyAction | 'create'; -export type ExcludeUpdateAction = AppendOnlyAction | 'remove'; -export type ExcludeRemoveAction = AppendOnlyAction | 'update'; -export type GenericAction = 'update' | ExcludeUpdateAction; -export type RelationAction = 'grant' | 'revoke'; +export declare type ReadOnlyAction = 'select' | 'count' | 'stat' | 'download' | 'aggregate'; +export declare type AppendOnlyAction = ReadOnlyAction | 'create'; +export declare type ExcludeUpdateAction = AppendOnlyAction | 'remove'; +export declare type ExcludeRemoveAction = AppendOnlyAction | 'update'; +export declare type GenericAction = 'update' | ExcludeUpdateAction; +export declare type RelationAction = 'grant' | 'revoke'; export declare const readOnlyActions: string[]; export declare const appendOnlyActions: string[]; export declare const excludeUpdateActions: string[]; export declare const exludeRemoveActions: string[]; export declare const genericActions: string[]; export declare const relationActions: string[]; -export type AbleAction = 'enable' | 'disable'; -export type AbleState = 'enabled' | 'disabled'; +export declare type AbleAction = 'enable' | 'disable'; +export declare type AbleState = 'enabled' | 'disabled'; export declare const makeAbleActionDef: (initialState?: AbleState) => ActionDef; diff --git a/lib/actions/action.js b/lib/actions/action.js index 2978756..0d384d2 100644 --- a/lib/actions/action.js +++ b/lib/actions/action.js @@ -1,7 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeAbleActionDef = exports.relationActions = exports.genericActions = exports.exludeRemoveActions = exports.excludeUpdateActions = exports.appendOnlyActions = exports.readOnlyActions = void 0; -exports.readOnlyActions = ['count', 'stat', 'download', 'select']; +exports.readOnlyActions = ['count', 'stat', 'download', 'select', 'aggregate']; exports.appendOnlyActions = exports.readOnlyActions.concat('create'); exports.excludeUpdateActions = exports.appendOnlyActions.concat('remove'); exports.exludeRemoveActions = exports.appendOnlyActions.concat('update'); diff --git a/lib/actions/relation.d.ts b/lib/actions/relation.d.ts index 2f74e02..d012b77 100644 --- a/lib/actions/relation.d.ts +++ b/lib/actions/relation.d.ts @@ -1,5 +1,5 @@ import { CascadeRelationItem, RelationHierarchy } from "../types/Entity"; -export type GenericRelation = 'owner'; +export declare type GenericRelation = 'owner'; export declare function convertHierarchyToAuth(hierarchy: RelationHierarchy): { [K in R]?: CascadeRelationItem; }; diff --git a/lib/base-app-domain/EntityDict.d.ts b/lib/base-app-domain/EntityDict.d.ts index b54b36e..cb81cf2 100644 --- a/lib/base-app-domain/EntityDict.d.ts +++ b/lib/base-app-domain/EntityDict.d.ts @@ -3,7 +3,7 @@ import { EntityDef as ModiEntity } from "./ModiEntity/Schema"; import { EntityDef as Oper } from "./Oper/Schema"; import { EntityDef as OperEntity } from "./OperEntity/Schema"; import { EntityDef as User } from "./User/Schema"; -export type EntityDict = { +export declare type EntityDict = { modi: Modi; modiEntity: ModiEntity; oper: Oper; diff --git a/lib/base-app-domain/Modi/Action.d.ts b/lib/base-app-domain/Modi/Action.d.ts index a114e7c..e22999c 100644 --- a/lib/base-app-domain/Modi/Action.d.ts +++ b/lib/base-app-domain/Modi/Action.d.ts @@ -1,9 +1,9 @@ import { ActionDef } from "../../types/Action"; import { GenericAction } from "../../actions/action"; -export type IState = 'active' | 'applied' | 'abandoned'; -export type IAction = 'apply' | 'abandon'; -export type ParticularAction = IAction; -export type Action = GenericAction | ParticularAction; +export declare type IState = 'active' | 'applied' | 'abandoned'; +export declare type IAction = 'apply' | 'abandon'; +export declare type ParticularAction = IAction; +export declare type Action = GenericAction | ParticularAction; export declare const actions: string[]; export declare const ActionDefDict: { iState: ActionDef; diff --git a/lib/base-app-domain/Modi/Action.js b/lib/base-app-domain/Modi/Action.js index 745f0a3..735e96e 100644 --- a/lib/base-app-domain/Modi/Action.js +++ b/lib/base-app-domain/Modi/Action.js @@ -8,7 +8,7 @@ var IActionDef = { }, is: 'active' }; -exports.actions = ["count", "stat", "download", "select", "create", "remove", "update", "apply", "abandon"]; +exports.actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "apply", "abandon"]; exports.ActionDefDict = { iState: IActionDef }; diff --git a/lib/base-app-domain/Modi/Schema.d.ts b/lib/base-app-domain/Modi/Schema.d.ts index 5874a91..915b675 100644 --- a/lib/base-app-domain/Modi/Schema.d.ts +++ b/lib/base-app-domain/Modi/Schema.d.ts @@ -2,11 +2,11 @@ import { String } 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, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; +import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; import { Action, ParticularAction, IState } from "./Action"; import * as ModiEntity from "../ModiEntity/Schema"; import * as OperEntity from "../OperEntity/Schema"; -export type OpSchema = EntityShape & { +export declare type OpSchema = EntityShape & { targetEntity: String<32>; entity: String<32>; entityId: String<64>; @@ -16,8 +16,8 @@ export type OpSchema = EntityShape & { extra?: Object | null; iState?: IState | null; }; -export type OpAttr = keyof OpSchema; -export type Schema = EntityShape & { +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { targetEntity: String<32>; entity: String<32>; entityId: String<64>; @@ -33,7 +33,7 @@ export type Schema = EntityShape & { } & { [A in ExpressionKey]?: any; }; -type AttrFilter = { +declare type AttrFilter = { id: Q_StringValue | SubQuery.ModiIdSubQuery; $$createAt$$: Q_DateValue; $$seq$$: Q_StringValue; @@ -47,8 +47,8 @@ type AttrFilter = { extra: Object; iState: Q_EnumValue; }; -export type Filter = MakeFilter>; -export type Projection = { +export declare type Filter = MakeFilter>; +export declare type Projection = { "#id"?: NodeId; [k: string]: any; id?: number; @@ -76,10 +76,10 @@ export type Projection = { $entity: "operEntity"; }; } & Partial>; -type ModiIdProjection = OneOf<{ +declare type ModiIdProjection = OneOf<{ id: number; }>; -export type SortAttr = { +export declare type SortAttr = { id: number; } | { $$createAt$$: number; @@ -100,15 +100,15 @@ export type SortAttr = { } | { [k: string]: any; } | OneOf>; -export type SortNode = { +export declare type SortNode = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -export type Sorter = SortNode[]; -export type SelectOperation

= Omit, "id">; -export type Selection

= Omit, "action">; -export type Aggregation = Omit, "id">; -export type CreateOperationData = FormCreateData> & ({ +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; @@ -116,20 +116,20 @@ export type CreateOperationData = FormCreateData[]> | Array>>; operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; -export type CreateSingleOperation = OakOperation<"create", CreateOperationData>; -export type CreateMultipleOperation = OakOperation<"create", Array>; -export type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -export type UpdateOperationData = FormUpdateData & { +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; - modiEntitys$modi?: OakOperation<"create", Omit[]> | Array>>; - operEntitys$entity?: OakOperation<"create", Omit[]> | Array>>; + modiEntity$modi?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; -export type UpdateOperation = OakOperation<"update" | ParticularAction | string, UpdateOperationData, Filter, Sorter>; -export type RemoveOperationData = {}; -export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; -export type Operation = CreateOperation | UpdateOperation | RemoveOperation; -export type ModiIdSubQuery = Selection; -export type EntityDef = { +export declare type UpdateOperation = OakOperation<"update" | ParticularAction | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {}; +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type ModiIdSubQuery = Selection; +export declare type EntityDef = { Schema: Schema; OpSchema: OpSchema; Action: OakMakeAction | string; diff --git a/lib/base-app-domain/ModiEntity/Schema.d.ts b/lib/base-app-domain/ModiEntity/Schema.d.ts index 98ea464..571f43b 100644 --- a/lib/base-app-domain/ModiEntity/Schema.d.ts +++ b/lib/base-app-domain/ModiEntity/Schema.d.ts @@ -2,17 +2,17 @@ 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, MakeAction as OakMakeAction, EntityShape } from "../../types/Entity"; +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 User from "../User/Schema"; -export type OpSchema = EntityShape & { +export declare type OpSchema = EntityShape & { modiId: ForeignKey<"modi">; entity: "user" | string; entityId: String<64>; }; -export type OpAttr = keyof OpSchema; -export type Schema = EntityShape & { +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { modiId: ForeignKey<"modi">; entity: "user" | string; entityId: String<64>; @@ -21,7 +21,7 @@ export type Schema = EntityShape & { } & { [A in ExpressionKey]?: any; }; -type AttrFilter = { +declare type AttrFilter = { id: Q_StringValue | SubQuery.ModiEntityIdSubQuery; $$createAt$$: Q_DateValue; $$seq$$: Q_StringValue; @@ -32,8 +32,8 @@ type AttrFilter = { entityId: Q_StringValue; user: User.Filter; }; -export type Filter> = MakeFilter & ExprOp>; -export type Projection = { +export declare type Filter> = MakeFilter & ExprOp>; +export declare type Projection = { "#id"?: NodeId; [k: string]: any; id?: number; @@ -46,16 +46,16 @@ export type Projection = { entityId?: number; user?: User.Projection; } & Partial>; -type ModiEntityIdProjection = OneOf<{ +declare type ModiEntityIdProjection = OneOf<{ id: number; }>; -type ModiIdProjection = OneOf<{ +declare type ModiIdProjection = OneOf<{ modiId: number; }>; -type UserIdProjection = OneOf<{ +declare type UserIdProjection = OneOf<{ entityId: number; }>; -export type SortAttr = { +export declare type SortAttr = { id: number; } | { $$createAt$$: number; @@ -76,15 +76,15 @@ export type SortAttr = { } | { [k: string]: any; } | OneOf>; -export type SortNode = { +export declare type SortNode = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -export type Sorter = SortNode[]; -export type SelectOperation

= Omit, "id">; -export type Selection

= Omit, "action">; -export type Aggregation = Omit, "id">; -export type CreateOperationData = FormCreateData> & (({ +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> & (({ modiId?: never; modi: Modi.CreateSingleOperation; } | { @@ -108,10 +108,10 @@ export type CreateOperationData = FormCreateData; -export type CreateMultipleOperation = OakOperation<"create", Array>; -export type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -export type UpdateOperationData = FormUpdateData> & (({ +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> & (({ modi: Modi.CreateSingleOperation; modiId?: never; } | { @@ -133,20 +133,20 @@ export type UpdateOperationData = FormUpdateData; -export type RemoveOperationData = {} & (({ +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & (({ modi?: Modi.UpdateOperation | Modi.RemoveOperation; })) & ({ user?: User.UpdateOperation | User.RemoveOperation; } | { [k: string]: any; }); -export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; -export type Operation = CreateOperation | UpdateOperation | RemoveOperation; -export type ModiIdSubQuery = Selection; -export type UserIdSubQuery = Selection; -export type ModiEntityIdSubQuery = Selection; -export type EntityDef = { +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type ModiIdSubQuery = Selection; +export declare type UserIdSubQuery = Selection; +export declare type ModiEntityIdSubQuery = Selection; +export declare type EntityDef = { Schema: Schema; OpSchema: OpSchema; Action: OakMakeAction | string; diff --git a/lib/base-app-domain/Oper/Schema.d.ts b/lib/base-app-domain/Oper/Schema.d.ts index 10e71b1..5a554b6 100644 --- a/lib/base-app-domain/Oper/Schema.d.ts +++ b/lib/base-app-domain/Oper/Schema.d.ts @@ -2,19 +2,19 @@ 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, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; +import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; import { AppendOnlyAction } from "../../actions/action"; import * as User from "../User/Schema"; import * as OperEntity from "../OperEntity/Schema"; -export type OpSchema = EntityShape & { +export declare type OpSchema = EntityShape & { action: String<16>; data: Object; filter?: Object | null; extra?: Object | null; operatorId?: ForeignKey<"user"> | null; }; -export type OpAttr = keyof OpSchema; -export type Schema = EntityShape & { +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { action: String<16>; data: Object; filter?: Object | null; @@ -26,7 +26,7 @@ export type Schema = EntityShape & { } & { [A in ExpressionKey]?: any; }; -type AttrFilter = { +declare type AttrFilter = { id: Q_StringValue | SubQuery.OperIdSubQuery; $$createAt$$: Q_DateValue; $$seq$$: Q_StringValue; @@ -38,8 +38,8 @@ type AttrFilter = { operatorId: Q_StringValue | SubQuery.UserIdSubQuery; operator: User.Filter; }; -export type Filter = MakeFilter>; -export type Projection = { +export declare type Filter = MakeFilter>; +export declare type Projection = { "#id"?: NodeId; [k: string]: any; id?: number; @@ -59,13 +59,13 @@ export type Projection = { $entity: "operEntity"; }; } & Partial>; -type OperIdProjection = OneOf<{ +declare type OperIdProjection = OneOf<{ id: number; }>; -type UserIdProjection = OneOf<{ +declare type UserIdProjection = OneOf<{ operatorId: number; }>; -export type SortAttr = { +export declare type SortAttr = { id: number; } | { $$createAt$$: number; @@ -82,15 +82,15 @@ export type SortAttr = { } | { [k: string]: any; } | OneOf>; -export type SortNode = { +export declare type SortNode = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -export type Sorter = SortNode[]; -export type SelectOperation

= Omit, "id">; -export type Selection

= Omit, "action">; -export type Aggregation = Omit, "id">; -export type CreateOperationData = FormCreateData> & (({ +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> & (({ operatorId?: never; operator?: User.CreateSingleOperation; } | { @@ -101,10 +101,10 @@ export type CreateOperationData = FormCreateData> & })) & { operEntity$oper?: OakOperation<"create", Omit[]> | Array>>; }; -export type CreateSingleOperation = OakOperation<"create", CreateOperationData>; -export type CreateMultipleOperation = OakOperation<"create", Array>; -export type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -export type UpdateOperationData = FormUpdateData> & (({ +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> & (({ operator: User.CreateSingleOperation; operatorId?: never; } | { @@ -118,17 +118,17 @@ export type UpdateOperationData = FormUpdateData> & operatorId?: String<64> | null; })) & { [k: string]: any; - operEntitys$oper?: OakOperation<"create", Omit[]> | Array>>; + operEntity$oper?: OakOperation<"create", Omit[]> | Array>>; }; -export type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; -export type RemoveOperationData = {} & (({ +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & (({ operator?: User.UpdateOperation | User.RemoveOperation; })); -export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; -export type Operation = CreateOperation | UpdateOperation | RemoveOperation; -export type UserIdSubQuery = Selection; -export type OperIdSubQuery = Selection; -export type EntityDef = { +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type UserIdSubQuery = Selection; +export declare type OperIdSubQuery = Selection; +export declare type EntityDef = { Schema: Schema; OpSchema: OpSchema; Action: OakMakeAction | string; diff --git a/lib/base-app-domain/OperEntity/Schema.d.ts b/lib/base-app-domain/OperEntity/Schema.d.ts index a500b12..77b67c6 100644 --- a/lib/base-app-domain/OperEntity/Schema.d.ts +++ b/lib/base-app-domain/OperEntity/Schema.d.ts @@ -2,18 +2,18 @@ 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, MakeAction as OakMakeAction, EntityShape } from "../../types/Entity"; +import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape } from "../../types/Entity"; import { AppendOnlyAction } from "../../actions/action"; import * as Oper from "../Oper/Schema"; import * as Modi from "../Modi/Schema"; import * as User from "../User/Schema"; -export type OpSchema = EntityShape & { +export declare type OpSchema = EntityShape & { operId: ForeignKey<"oper">; entity: "modi" | "user" | string; entityId: String<64>; }; -export type OpAttr = keyof OpSchema; -export type Schema = EntityShape & { +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { operId: ForeignKey<"oper">; entity: "modi" | "user" | string; entityId: String<64>; @@ -23,7 +23,7 @@ export type Schema = EntityShape & { } & { [A in ExpressionKey]?: any; }; -type AttrFilter = { +declare type AttrFilter = { id: Q_StringValue | SubQuery.OperEntityIdSubQuery; $$createAt$$: Q_DateValue; $$seq$$: Q_StringValue; @@ -35,8 +35,8 @@ type AttrFilter = { modi: Modi.Filter; user: User.Filter; }; -export type Filter> = MakeFilter & ExprOp>; -export type Projection = { +export declare type Filter> = MakeFilter & ExprOp>; +export declare type Projection = { "#id"?: NodeId; [k: string]: any; id?: number; @@ -50,19 +50,19 @@ export type Projection = { modi?: Modi.Projection; user?: User.Projection; } & Partial>; -type OperEntityIdProjection = OneOf<{ +declare type OperEntityIdProjection = OneOf<{ id: number; }>; -type OperIdProjection = OneOf<{ +declare type OperIdProjection = OneOf<{ operId: number; }>; -type ModiIdProjection = OneOf<{ +declare type ModiIdProjection = OneOf<{ entityId: number; }>; -type UserIdProjection = OneOf<{ +declare type UserIdProjection = OneOf<{ entityId: number; }>; -export type SortAttr = { +export declare type SortAttr = { id: number; } | { $$createAt$$: number; @@ -85,15 +85,15 @@ export type SortAttr = { } | { [k: string]: any; } | OneOf>; -export type SortNode = { +export declare type SortNode = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -export type Sorter = SortNode[]; -export type SelectOperation

= Omit, "id">; -export type Selection

= Omit, "action">; -export type Aggregation = Omit, "id">; -export type CreateOperationData = FormCreateData> & (({ +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> & (({ operId?: never; oper: Oper.CreateSingleOperation; } | { @@ -125,10 +125,10 @@ export type CreateOperationData = FormCreateData; -export type CreateMultipleOperation = OakOperation<"create", Array>; -export type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -export type UpdateOperationData = FormUpdateData> & (({ +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> & (({ oper: Oper.CreateSingleOperation; operId?: never; } | { @@ -148,21 +148,21 @@ export type UpdateOperationData = FormUpdateData; -export type RemoveOperationData = {} & ({ +export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {} & ({ modi?: Modi.UpdateOperation | Modi.RemoveOperation; } | { user?: User.UpdateOperation | User.RemoveOperation; } | { [k: string]: any; }); -export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; -export type Operation = CreateOperation | UpdateOperation | RemoveOperation; -export type OperIdSubQuery = Selection; -export type ModiIdSubQuery = Selection; -export type UserIdSubQuery = Selection; -export type OperEntityIdSubQuery = Selection; -export type EntityDef = { +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type OperIdSubQuery = Selection; +export declare type ModiIdSubQuery = Selection; +export declare type UserIdSubQuery = Selection; +export declare type OperEntityIdSubQuery = Selection; +export declare type EntityDef = { Schema: Schema; OpSchema: OpSchema; Action: OakMakeAction | string; diff --git a/lib/base-app-domain/User/Schema.d.ts b/lib/base-app-domain/User/Schema.d.ts index 3bc5d51..54a81c5 100644 --- a/lib/base-app-domain/User/Schema.d.ts +++ b/lib/base-app-domain/User/Schema.d.ts @@ -2,18 +2,18 @@ import { String, Text } 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, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; +import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, EntityShape, AggregationResult } from "../../types/Entity"; import { GenericAction, RelationAction } from "../../actions/action"; import * as Oper from "../Oper/Schema"; import * as OperEntity from "../OperEntity/Schema"; import * as ModiEntity from "../ModiEntity/Schema"; -export type OpSchema = EntityShape & { +export declare type OpSchema = EntityShape & { name?: String<16> | null; nickname?: String<64> | null; password?: Text | null; }; -export type OpAttr = keyof OpSchema; -export type Schema = EntityShape & { +export declare type OpAttr = keyof OpSchema; +export declare type Schema = EntityShape & { name?: String<16> | null; nickname?: String<64> | null; password?: Text | null; @@ -26,7 +26,7 @@ export type Schema = EntityShape & { } & { [A in ExpressionKey]?: any; }; -type AttrFilter = { +declare type AttrFilter = { id: Q_StringValue | SubQuery.UserIdSubQuery; $$createAt$$: Q_DateValue; $$seq$$: Q_StringValue; @@ -35,8 +35,8 @@ type AttrFilter = { nickname: Q_StringValue; password: Q_StringValue; }; -export type Filter = MakeFilter>; -export type Projection = { +export declare type Filter = MakeFilter>; +export declare type Projection = { "#id"?: NodeId; [k: string]: any; id?: number; @@ -65,10 +65,10 @@ export type Projection = { $entity: "modiEntity"; }; } & Partial>; -type UserIdProjection = OneOf<{ +declare type UserIdProjection = OneOf<{ id: number; }>; -export type SortAttr = { +export declare type SortAttr = { id: number; } | { $$createAt$$: number; @@ -85,34 +85,34 @@ export type SortAttr = { } | { [k: string]: any; } | OneOf>; -export type SortNode = { +export declare type SortNode = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -export type Sorter = SortNode[]; -export type SelectOperation

= Omit, "id">; -export type Selection

= Omit, "action">; -export type Aggregation = Omit, "id">; -export type CreateOperationData = FormCreateData & { +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 & { oper$operator?: OakOperation<"create", Omit[]> | Array>>; operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; -export type CreateSingleOperation = OakOperation<"create", CreateOperationData>; -export type CreateMultipleOperation = OakOperation<"create", Array>; -export type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -export type UpdateOperationData = FormUpdateData & { +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; - opers$operator?: OakOperation<"create", Omit[]> | Array>>; - operEntitys$entity?: OakOperation<"create", Omit[]> | Array>>; - modiEntitys$entity?: OakOperation<"create", Omit[]> | Array>>; + oper$operator?: OakOperation<"create", Omit[]> | Array>>; + operEntity$entity?: OakOperation<"create", Omit[]> | Array>>; + modiEntity$entity?: OakOperation<"create", Omit[]> | Array>>; }; -export type UpdateOperation = OakOperation<"update" | RelationAction | string, UpdateOperationData, Filter, Sorter>; -export type RemoveOperationData = {}; -export type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; -export type Operation = CreateOperation | UpdateOperation | RemoveOperation; -export type UserIdSubQuery = Selection; -export type EntityDef = { +export declare type UpdateOperation = OakOperation<"update" | RelationAction | string, UpdateOperationData, Filter, Sorter>; +export declare type RemoveOperationData = {}; +export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>; +export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type UserIdSubQuery = Selection; +export declare type EntityDef = { Schema: Schema; OpSchema: OpSchema; Action: OakMakeAction | string; diff --git a/lib/base-app-domain/_SubQuery.d.ts b/lib/base-app-domain/_SubQuery.d.ts index 44e0c13..4b68ed6 100644 --- a/lib/base-app-domain/_SubQuery.d.ts +++ b/lib/base-app-domain/_SubQuery.d.ts @@ -3,31 +3,31 @@ import * as ModiEntity from "./ModiEntity/Schema"; import * as Oper from "./Oper/Schema"; import * as OperEntity from "./OperEntity/Schema"; import * as User from "./User/Schema"; -export type ModiIdSubQuery = { +export declare type ModiIdSubQuery = { [K in "$in" | "$nin"]?: (ModiEntity.ModiIdSubQuery & { entity: "modiEntity"; }) | (Modi.ModiIdSubQuery & { entity: "modi"; }) | any; }; -export type ModiEntityIdSubQuery = { +export declare type ModiEntityIdSubQuery = { [K in "$in" | "$nin"]?: (ModiEntity.ModiEntityIdSubQuery & { entity: "modiEntity"; }) | any; }; -export type OperIdSubQuery = { +export declare type OperIdSubQuery = { [K in "$in" | "$nin"]?: (OperEntity.OperIdSubQuery & { entity: "operEntity"; }) | (Oper.OperIdSubQuery & { entity: "oper"; }) | any; }; -export type OperEntityIdSubQuery = { +export declare type OperEntityIdSubQuery = { [K in "$in" | "$nin"]?: (OperEntity.OperEntityIdSubQuery & { entity: "operEntity"; }) | any; }; -export type UserIdSubQuery = { +export declare type UserIdSubQuery = { [K in "$in" | "$nin"]?: (Oper.UserIdSubQuery & { entity: "oper"; }) | (User.UserIdSubQuery & { diff --git a/lib/compiler/schemalBuilder.js b/lib/compiler/schemalBuilder.js index 2a3d8b2..b493670 100644 --- a/lib/compiler/schemalBuilder.js +++ b/lib/compiler/schemalBuilder.js @@ -652,12 +652,12 @@ function analyzeEntity(filename, path, program, relativePath) { return ele2.name.getText() === 'name'; }); (0, assert_1.default)(ts.isStringLiteral(nameProperty.initializer)); - var indexName = nameProperty.initializer.text; - if (indexNameDict_1[indexName]) { - throw new Error("\u300C".concat(filename, "\u300D\u7D22\u5F15\u5B9A\u4E49\u91CD\u540D\u300C").concat(indexName, "\u300D")); + var nameText = nameProperty.initializer.text; + if (indexNameDict_1[nameText]) { + throw new Error("\u300C".concat(filename, "\u300D\u7D22\u5F15\u5B9A\u4E49\u91CD\u540D\u300C").concat(nameText, "\u300D")); } (0, lodash_1.assign)(indexNameDict_1, (_a = {}, - _a[indexName] = true, + _a[nameText] = true, _a)); var configProperty = properties.find(function (ele2) { (0, assert_1.default)(ts.isPropertyAssignment(ele2)); @@ -695,10 +695,10 @@ function analyzeEntity(filename, path, program, relativePath) { return ele3.name.text === indexAttrName; }); if (!schemaNode) { - throw new Error("\u300C".concat(filename, "\u300D\u4E2D\u7D22\u5F15\u300C").concat(indexName, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u5B9A\u4E49\u975E\u6CD5")); + throw new Error("\u300C".concat(filename, "\u300D\u4E2D\u7D22\u5F15\u300C").concat(nameText, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u5B9A\u4E49\u975E\u6CD5")); } var type = schemaNode.type, name_1 = schemaNode.name; - var entity = (0, string_1.firstLetterLowerCase)(moduleName); + var entity = moduleName; var _a = ManyToOne, _b = entity, manyToOneSet = _a[_b]; if (ts.isTypeReferenceNode(type)) { var typeName = type.typeName; @@ -712,14 +712,19 @@ function analyzeEntity(filename, path, program, relativePath) { if (!manyToOneItem) { // 如果不是外键,则不能是Text, File if (isFulltextIndex) { - (0, assert_1.default)(['Text', 'String'].includes(text2_1), "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(indexName, "\u300D\u5B9A\u4E49\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u975E\u6CD5\uFF0C\u53EA\u80FD\u662FText/String")); + (0, assert_1.default)(['Text', 'String'].includes(text2_1), "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(nameText, "\u300D\u5B9A\u4E49\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u975E\u6CD5\uFF0C\u53EA\u80FD\u662FText/String")); } else { - (0, assert_1.default)(!DataType_1.unIndexedTypes.includes(text2_1), "\u300C".concat(filename, "\u300D\u4E2D\u7D22\u5F15\u300C").concat(indexName, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7684\u7C7B\u578B\u4E3A\u300C").concat(text2_1, "\u300D\uFF0C\u4E0D\u53EF\u7D22\u5F15")); + (0, assert_1.default)(!DataType_1.unIndexedTypes.includes(text2_1), "\u300C".concat(filename, "\u300D\u4E2D\u7D22\u5F15\u300C").concat(nameText, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7684\u7C7B\u578B\u4E3A\u300C").concat(text2_1, "\u300D\uFF0C\u4E0D\u53EF\u7D22\u5F15")); } } else { - (0, assert_1.default)(!isFulltextIndex, "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(indexName, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u975E\u6CD5\uFF0C\u53EA\u80FD\u4E3AText/String")); + (0, assert_1.default)(!isFulltextIndex, "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(nameText, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u975E\u6CD5\uFF0C\u53EA\u80FD\u4E3AText/String")); + // 在这里把外键加上Id,这样storageSchema才能正常通过 + // 这里的写法不太好,未来TS版本高了可能会有问题。by Xc 20230131 + Object.assign(nameProperty, { + initializer: factory.createStringLiteral("".concat(indexAttrName, "Id")), + }); } } else { @@ -727,8 +732,8 @@ function analyzeEntity(filename, path, program, relativePath) { } } else { - (0, assert_1.default)(!isFulltextIndex, "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(indexName, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u53EA\u80FD\u4E3AText/String")); - (0, assert_1.default)(ts.isUnionTypeNode(type) || ts.isLiteralTypeNode(type), "".concat(entity, "\u4E2D\u7D22\u5F15\u300C").concat(indexName, "\u300D\u7684\u5C5E\u6027").concat(name_1.text, "\u6709\u5B9A\u4E49\u975E\u6CD5")); + (0, assert_1.default)(!isFulltextIndex, "\u300C".concat(filename, "\u300D\u4E2D\u5168\u6587\u7D22\u5F15\u300C").concat(nameText, "\u300D\u7684\u5C5E\u6027\u300C").concat(indexAttrName, "\u300D\u7C7B\u578B\u53EA\u80FD\u4E3AText/String")); + (0, assert_1.default)(ts.isUnionTypeNode(type) || ts.isLiteralTypeNode(type), "".concat(entity, "\u4E2D\u7D22\u5F15\u300C").concat(nameText, "\u300D\u7684\u5C5E\u6027").concat(name_1.text, "\u6709\u5B9A\u4E49\u975E\u6CD5")); } } }); @@ -1077,6 +1082,7 @@ function constructFilter(statements, entity) { break; } case 'Int': + case 'Uint': case 'Float': case 'Double': { type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_NumberValue')); @@ -1784,14 +1790,11 @@ function constructActions(statements, entity) { // 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("Omit"), [ - factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [ - factory.createLiteralTypeNode(factory.createStringLiteral("select")), - factory.createTypeReferenceNode(factory.createIdentifier("P"), undefined), - factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined), - factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined) - ]), - factory.createLiteralTypeNode(factory.createStringLiteral("id")) + ], 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"), [ @@ -1799,13 +1802,10 @@ function constructActions(statements, entity) { 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("Omit"), [ - factory.createTypeReferenceNode(factory.createIdentifier("DeduceAggregation"), [ - factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined), - factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined), - factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined) - ]), - factory.createLiteralTypeNode(factory.createStringLiteral("id")) + ])), 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 @@ -2297,7 +2297,7 @@ function constructActions(statements, entity) { var _loop_9 = function (entityName) { var entityNameLc = (0, string_1.firstLetterLowerCase)(entityName); foreignKeySet[entityName].forEach(function (foreignKey) { - var identifier = "".concat(entityNameLc, "s$").concat(foreignKey); + var identifier = "".concat(entityNameLc, "$").concat(foreignKey); var otmCreateOperationDataNode = factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [ factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'CreateOperationData'), undefined), factory.createUnionTypeNode(foreignKey === 'entity' ? [ @@ -2612,6 +2612,7 @@ var initialStatements = function () { return [ 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')), @@ -2653,6 +2654,7 @@ var initialStatements = function () { return [ 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")), diff --git a/lib/store/CascadeStore.js b/lib/store/CascadeStore.js index 551b770..5f923df 100644 --- a/lib/store/CascadeStore.js +++ b/lib/store/CascadeStore.js @@ -529,7 +529,7 @@ var CascadeStore = /** @class */ (function (_super) { var _g = tslib_1.__read(relation, 2), entityOtm_1 = _g[0], foreignKey_2 = _g[1]; var otmOperations = data[attr]; var dealWithOneToMany = function (otm) { - var _a, _b, _c, _d; + var _a, _b, _c, _d, _e; var actionOtm = otm.action, dataOtm = otm.data, filterOtm = otm.filter; if (!foreignKey_2) { // 基于entity/entityId的one-to-many @@ -550,7 +550,7 @@ var CascadeStore = /** @class */ (function (_super) { } else if (actionOtm === 'create') { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // todo 这个假设成立吗?等遇到create/create一对多的case再完善 + // todo 这个假设对watcher等后台行为可能不成立,等遇到create/create一对多的case再完善 var id_2 = filter.id; (0, assert_1.default)(typeof id_2 === 'string'); if (dataOtm instanceof Array) { @@ -569,12 +569,10 @@ var CascadeStore = /** @class */ (function (_super) { else { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 // 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善 - var id = filter.id; Object.assign(otm, { - filter: (0, filter_1.addFilterSegment)({ - entity: entity, - entityId: id, - }, filterOtm), + filter: (0, filter_1.addFilterSegment)((_a = {}, + _a[entity] = filter, + _a), filterOtm), }); if (action === 'remove' && actionOtm === 'update') { Object.assign(dataOtm, { @@ -597,14 +595,14 @@ var CascadeStore = /** @class */ (function (_super) { }); } else { - Object.assign(dataOtm, (_a = {}, - _a[foreignKey_2] = id_3, - _a)); + Object.assign(dataOtm, (_b = {}, + _b[foreignKey_2] = id_3, + _b)); } } else if (actionOtm === 'create') { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // todo 这个假设成立吗?等遇到create/create一对多的case再完善 + // todo 这个假设在后台可能不成立,等遇到了再说 var id_4 = filter.id; (0, assert_1.default)(typeof id_4 === 'string'); if (dataOtm instanceof Array) { @@ -616,28 +614,26 @@ var CascadeStore = /** @class */ (function (_super) { }); } else { - Object.assign(dataOtm, (_b = {}, - _b[foreignKey_2] = id_4, - _b)); + Object.assign(dataOtm, (_c = {}, + _c[foreignKey_2] = id_4, + _c)); } } else { - // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善 - var id = filter.id; + // update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger) Object.assign(otm, { - filter: (0, filter_1.addFilterSegment)((_c = {}, - _c[foreignKey_2] = id, - _c), filterOtm), + filter: (0, filter_1.addFilterSegment)((_d = {}, + _d[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter, + _d), filterOtm), }); if (action === 'remove' && actionOtm === 'update') { - Object.assign(dataOtm, (_d = {}, - _d[foreignKey_2] = null, - _d)); + Object.assign(dataOtm, (_e = {}, + _e[foreignKey_2] = null, + _e)); } } } - afterFns.push(function () { return cascadeUpdate.call(_this, entityOtm_1, otm, context, option2); }); + beforeFns.push(function () { return cascadeUpdate.call(_this, entityOtm_1, otm, context, option2); }); }; if (otmOperations instanceof Array) { try { diff --git a/lib/store/TriggerExecutor.d.ts b/lib/store/TriggerExecutor.d.ts index 77253f7..d63a7ed 100644 --- a/lib/store/TriggerExecutor.d.ts +++ b/lib/store/TriggerExecutor.d.ts @@ -16,7 +16,7 @@ export declare class TriggerExecutor { private contextBuilder; constructor(contextBuilder: (cxtString: string) => Promise>, logger?: Logger); registerChecker>(checker: Checker): void; - getCheckers(entity: T, action: ED[T]['Action'], checkerTypes?: CheckerType[]): Trigger>[]; + getCheckers(entity: T, action: ED[T]['Action'], checkerTypes?: CheckerType[]): Trigger>[] | undefined; registerTrigger>(trigger: Trigger): void; unregisterTrigger>(trigger: Trigger): void; private preCommitTrigger; diff --git a/lib/store/actionDef.js b/lib/store/actionDef.js index 06c08de..4994337 100644 --- a/lib/store/actionDef.js +++ b/lib/store/actionDef.js @@ -2,6 +2,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.analyzeActionDefDict = exports.getFullProjection = void 0; var tslib_1 = require("tslib"); +var types_1 = require("../types"); +var lodash_1 = require("../utils/lodash"); +var filter_1 = require("./filter"); function getFullProjection(entity, schema) { var attributes = schema[entity].attributes; var projection = { @@ -20,56 +23,116 @@ function getFullProjection(entity, schema) { } exports.getFullProjection = getFullProjection; function makeIntrinsicWatchers(schema) { - var _this = this; var watchers = []; - var _loop_1 = function (entity) { + for (var entity in schema) { var attributes = schema[entity].attributes; - var now = Date.now(); var expiresAt = attributes.expiresAt, expired = attributes.expired; if (expiresAt && expiresAt.type === 'datetime' && expired && expired.type === 'boolean') { // 如果有定义expiresAt和expired,则自动生成一个检查的watcher watchers.push({ entity: entity, name: "\u5BF9\u8C61".concat(entity, "\u4E0A\u7684\u8FC7\u671F\u81EA\u52A8watcher"), - filter: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { - return tslib_1.__generator(this, function (_a) { - return [2 /*return*/, { - expired: false, - expiresAt: { - $lte: now, - }, - }]; - }); - }); }, + filter: function () { + return { + expired: false, + expiresAt: { + $lte: Date.now(), + }, + }; + }, action: 'update', actionData: { expired: true, }, }); } - }; - for (var entity in schema) { - _loop_1(entity); } return watchers; } +function checkUniqueBetweenRows(rows, uniqAttrs) { + // 先检查这些行本身之间有无unique冲突 + var uniqRows = (0, lodash_1.uniqBy)(rows, function (d) { + var e_1, _a; + var s = ''; + try { + for (var uniqAttrs_1 = tslib_1.__values(uniqAttrs), uniqAttrs_1_1 = uniqAttrs_1.next(); !uniqAttrs_1_1.done; uniqAttrs_1_1 = uniqAttrs_1.next()) { + var a = uniqAttrs_1_1.value; + if (d[a] === null || d[a] === undefined) { + s + d.id; + } + else { + s + "-".concat(d[a]); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (uniqAttrs_1_1 && !uniqAttrs_1_1.done && (_a = uniqAttrs_1.return)) _a.call(uniqAttrs_1); + } + finally { if (e_1) throw e_1.error; } + } + return s; + }); + if (uniqRows.length < rows.length) { + throw new types_1.OakUniqueViolationException([{ + attrs: uniqAttrs, + }]); + } +} +function checkCountLessThan(count, uniqAttrs, than, id) { + if (than === void 0) { than = 0; } + if (count instanceof Promise) { + return count.then(function (count2) { + if (count2 > than) { + throw new types_1.OakUniqueViolationException([{ + id: id, + attrs: uniqAttrs, + }]); + } + }); + } + if (count > than) { + throw new types_1.OakUniqueViolationException([{ + id: id, + attrs: uniqAttrs, + }]); + } +} +function checkUnique(entity, row, context, uniqAttrs, extraFilter) { + var filter = (0, lodash_1.pick)(row, uniqAttrs); + for (var a in filter) { + if (filter[a] === null || filter[a] === undefined) { + delete filter[a]; + } + } + if (Object.keys(filter).length < uniqAttrs.length) { + // 说明有null值,不需要检查约束 + return; + } + var filter2 = extraFilter ? (0, filter_1.addFilterSegment)([filter, extraFilter]) : filter; + var count = context.count(entity, { filter: filter2 }, { dontCollect: true }); + return checkCountLessThan(count, uniqAttrs, 0, row.id); +} function analyzeActionDefDict(schema, actionDefDict) { + var _a; var checkers = []; var triggers = []; + // action状态转换矩阵相应的checker for (var entity in actionDefDict) { - var _loop_2 = function (attr) { + var _loop_1 = function (attr) { var def = actionDefDict[entity][attr]; - var _a = def, stm = _a.stm, is = _a.is; + var _b = def, stm = _b.stm, is = _b.is; var _loop_3 = function (action) { - var _b, _c; + var _c, _d; var actionStm = stm[action]; - var conditionalFilter = typeof actionStm[0] === 'string' ? (_b = {}, - _b[attr] = actionStm[0], - _b) : (_c = {}, - _c[attr] = { + var conditionalFilter = typeof actionStm[0] === 'string' ? (_c = {}, + _c[attr] = actionStm[0], + _c) : (_d = {}, + _d[attr] = { $in: actionStm[0], }, - _c); + _d); checkers.push({ action: action, type: 'row', @@ -123,9 +186,144 @@ function analyzeActionDefDict(schema, actionDefDict) { } }; for (var attr in actionDefDict[entity]) { - _loop_2(attr); + _loop_1(attr); } } + var _loop_2 = function (entity) { + var e_2, _e; + var indexes = schema[entity].indexes; + if (indexes) { + var _loop_4 = function (index) { + if ((_a = index.config) === null || _a === void 0 ? void 0 : _a.unique) { + var attributes = index.attributes; + var uniqAttrs_2 = attributes.map(function (ele) { return ele.name; }); + checkers.push({ + entity: entity, + action: 'create', + type: 'logical', + priority: 20, + checker: function (operation, context) { + var data = operation.data; + if (data instanceof Array) { + checkUniqueBetweenRows(data, uniqAttrs_2); + var checkResult = data.map(function (ele) { return checkUnique(entity, ele, context, uniqAttrs_2); }); + if (checkResult[0] instanceof Promise) { + return Promise.all(checkResult).then(function () { return undefined; }); + } + } + else { + return checkUnique(entity, data, context, uniqAttrs_2); + } + } + }, { + entity: entity, + action: 'update', + type: 'logical', + priority: 20, + checker: function (operation, context) { + var e_3, _a, e_4, _b, _c; + var _d = operation, data = _d.data, operationFilter = _d.filter; + var attrs = Object.keys(data); + var refAttrs = (0, lodash_1.intersection)(attrs, uniqAttrs_2); + if (refAttrs.length === 0) { + // 如果本次更新和unique约束的属性之间没有交集则直接返回 + return; + } + try { + for (var refAttrs_1 = (e_3 = void 0, tslib_1.__values(refAttrs)), refAttrs_1_1 = refAttrs_1.next(); !refAttrs_1_1.done; refAttrs_1_1 = refAttrs_1.next()) { + var attr = refAttrs_1_1.value; + // 如果有更新为null值,不用再检查约束 + if (data[attr] === null || data[attr] === undefined) { + return; + } + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (refAttrs_1_1 && !refAttrs_1_1.done && (_a = refAttrs_1.return)) _a.call(refAttrs_1); + } + finally { if (e_3) throw e_3.error; } + } + if (refAttrs.length === uniqAttrs_2.length) { + // 如果更新了全部属性,直接检查 + var filter = (0, lodash_1.pick)(data, refAttrs); + // 在这些行以外的行不和更新后的键值冲突 + var count = context.count(entity, { + filter: (0, filter_1.addFilterSegment)([filter, { + $not: operationFilter, + }]), + }, { dontCollect: true }); + var checkCount = checkCountLessThan(count, uniqAttrs_2); + // 更新的行只能有一行 + var rowCount = context.count(entity, { + filter: operationFilter, + }, { dontCollect: true }); + var checkRowCount = checkCountLessThan(rowCount, uniqAttrs_2, 1); + // 如果更新的行数为零似乎也可以,但这应该不可能出现吧,by Xc 20230131 + if (checkRowCount instanceof Promise) { + return Promise.all([checkCount, checkRowCount]).then(function () { return undefined; }); + } + } + // 否则需要结合本行现有的属性来进行检查 + var projection = { id: 1 }; + try { + for (var uniqAttrs_3 = (e_4 = void 0, tslib_1.__values(uniqAttrs_2)), uniqAttrs_3_1 = uniqAttrs_3.next(); !uniqAttrs_3_1.done; uniqAttrs_3_1 = uniqAttrs_3.next()) { + var attr = uniqAttrs_3_1.value; + Object.assign(projection, (_c = {}, + _c[attr] = 1, + _c)); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (uniqAttrs_3_1 && !uniqAttrs_3_1.done && (_b = uniqAttrs_3.return)) _b.call(uniqAttrs_3); + } + finally { if (e_4) throw e_4.error; } + } + var checkWithRows = function (rows2) { + var rows22 = rows2.map(function (ele) { return Object.assign(ele, data); }); + // 先检查这些行本身之间是否冲突 + checkUniqueBetweenRows(rows22, uniqAttrs_2); + var checkResults = rows22.map(function (row) { return checkUnique(entity, row, context, uniqAttrs_2, { + $not: operationFilter + }); }); + if (checkResults[0] instanceof Promise) { + return Promise.all(checkResults).then(function () { return undefined; }); + } + }; + var currentRows = context.select(entity, { + data: projection, + filter: operationFilter, + }, { dontCollect: true }); + if (currentRows instanceof Promise) { + return currentRows.then(function (row2) { return checkWithRows(row2); }); + } + return checkWithRows(currentRows); + } + }); + } + }; + try { + for (var indexes_1 = (e_2 = void 0, tslib_1.__values(indexes)), indexes_1_1 = indexes_1.next(); !indexes_1_1.done; indexes_1_1 = indexes_1.next()) { + var index = indexes_1_1.value; + _loop_4(index); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (indexes_1_1 && !indexes_1_1.done && (_e = indexes_1.return)) _e.call(indexes_1); + } + finally { if (e_2) throw e_2.error; } + } + } + }; + // unique索引相应的checker + for (var entity in schema) { + _loop_2(entity); + } return { triggers: triggers, checkers: checkers, diff --git a/lib/types/Action.d.ts b/lib/types/Action.d.ts index 6bba1b7..242e274 100644 --- a/lib/types/Action.d.ts +++ b/lib/types/Action.d.ts @@ -1,18 +1,18 @@ import { CascadeRelationItem, EntityDict } from "./Entity"; import { GenericAction } from '../actions/action'; -export type Action = string; -export type State = string; -export type ActionDef = { +export declare type Action = string; +export declare type State = string; +export declare type ActionDef = { stm: { [a in A]: [p: S | S[], n: S]; }; is?: S; }; -export type ActionDictOfEntityDict = { +export declare type ActionDictOfEntityDict = { [T in keyof E]?: { [A in keyof E[T]['OpSchema']]?: ActionDef; }; }; -export type CascadeActionAuth = { +export declare type CascadeActionAuth = { [K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; }; diff --git a/lib/types/Auth.d.ts b/lib/types/Auth.d.ts index 862f12c..cee2d7b 100644 --- a/lib/types/Auth.d.ts +++ b/lib/types/Auth.d.ts @@ -2,12 +2,12 @@ import { CascadeActionAuth, CascadeRelationAuth } from "."; import { AsyncContext } from "../store/AsyncRowStore"; import { SyncContext } from "../store/SyncRowStore"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; -export type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation'; +export declare type CheckerType = 'relation' | 'row' | 'data' | 'logical' | 'logicalRelation'; /** * conditionalFilter是指该action发生时,operation所操作的行中有满足conditionalFilter的行 * 被转化成trigger的filter条件,详细可看trigger中的说明 */ -export type DataChecker | SyncContext> = { +export declare type DataChecker | SyncContext> = { priority?: number; type: 'data'; entity: T; @@ -15,7 +15,7 @@ export type DataChecker void | Promise; conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter'] | Promise); }; -export type RowChecker | SyncContext> = { +export declare type RowChecker | SyncContext> = { priority?: number; type: 'row'; entity: T; @@ -28,7 +28,7 @@ export type RowChecker ED[T]['Update']['filter'] | Promise); }; -export type RelationChecker | SyncContext> = { +export declare type RelationChecker | SyncContext> = { priority?: number; type: 'relation'; entity: T; @@ -38,7 +38,7 @@ export type RelationChecker ED[T]['Update']['filter'] | Promise); }; -export type LogicalChecker | SyncContext> = { +export declare type LogicalChecker | SyncContext> = { priority?: number; type: 'logical'; when?: 'after'; @@ -47,7 +47,7 @@ export type LogicalChecker void | Promise; conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); }; -export type LogicalRelationChecker | SyncContext> = { +export declare type LogicalRelationChecker | SyncContext> = { priority?: number; type: 'logicalRelation'; when?: 'after'; @@ -56,11 +56,11 @@ export type LogicalRelationChecker void | Promise; conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); }; -export type Checker | SyncContext> = DataChecker | RowChecker | RelationChecker | LogicalChecker | LogicalRelationChecker; -export type AuthDef = { +export declare type Checker | SyncContext> = DataChecker | RowChecker | RelationChecker | LogicalChecker | LogicalRelationChecker; +export declare type AuthDef = { relationAuth?: CascadeRelationAuth>; actionAuth?: CascadeActionAuth; }; -export type AuthDefDict = { +export declare type AuthDefDict = { [K in keyof ED]?: AuthDef; }; diff --git a/lib/types/DataType.d.ts b/lib/types/DataType.d.ts index 63eaa1e..1c81265 100644 --- a/lib/types/DataType.d.ts +++ b/lib/types/DataType.d.ts @@ -1,18 +1,18 @@ import { Geo, SingleGeo } from "./Geo"; -export type Int = number; -export type Uint = number; -export type Double

= number; -export type Float

= number; -export type String = string; -export type Text = string; -export type Image = string; -export type File = string; -export type Datetime = number | Date; -export type Boolean = boolean; -export type PrimaryKey = string; -export type ForeignKey = string; -export type Sequence = string; +export declare type Int = number; +export declare type Uint = number; +export declare type Double

= number; +export declare type Float

= number; +export declare type String = string; +export declare type Text = string; +export declare type Image = string; +export declare type File = string; +export declare type Datetime = number | Date; +export declare type Boolean = boolean; +export declare type PrimaryKey = string; +export declare type ForeignKey = string; +export declare type Sequence = string; export { Geo, SingleGeo } from './Geo'; -export type DataTypes = number | string | Datetime | Geo | Object | SingleGeo; +export declare type DataTypes = number | string | Datetime | Geo | Object | SingleGeo; export declare const types: string[]; export declare const unIndexedTypes: string[]; diff --git a/lib/types/Demand.d.ts b/lib/types/Demand.d.ts index 041672f..c2605fe 100644 --- a/lib/types/Demand.d.ts +++ b/lib/types/Demand.d.ts @@ -1,12 +1,12 @@ import { RefOrExpression } from "./Expression"; import { OneOf } from "./Polyfill"; export declare const EXPRESSION_PREFIX = "$expr"; -export type NodeId = `node-${number}`; -export type ExpressionKey = '$expr' | '$expr1' | '$expr2' | '$expr3' | '$expr4' | '$expr5' | '$expr6' | '$expr7' | '$expr8' | '$expr9' | '$expr10' | '$expr11' | '$expr12' | '$expr13' | '$expr14' | '$expr15' | '$expr16' | '$expr17' | '$expr18' | '$expr19' | '$expr20'; -export type ExprOp = { +export declare type NodeId = `node-${number}`; +export declare type ExpressionKey = '$expr' | '$expr1' | '$expr2' | '$expr3' | '$expr4' | '$expr5' | '$expr6' | '$expr7' | '$expr8' | '$expr9' | '$expr10' | '$expr11' | '$expr12' | '$expr13' | '$expr14' | '$expr15' | '$expr16' | '$expr17' | '$expr18' | '$expr19' | '$expr20'; +export declare type ExprOp = { [K in ExpressionKey]: RefOrExpression; }; -export type Q_NumberComparisonValue = number | OneOf<{ +export declare type Q_NumberComparisonValue = number | OneOf<{ $gt: number; $lt: number; $gte: number; @@ -17,7 +17,7 @@ export type Q_NumberComparisonValue = number | OneOf<{ $nin: number[]; $between: [number, number]; }>; -export type Q_StringComparisonValue = string | OneOf<{ +export declare type Q_StringComparisonValue = string | OneOf<{ $gt: string; $lt: string; $gte: string; @@ -30,44 +30,44 @@ export type Q_StringComparisonValue = string | OneOf<{ $in: string[]; $nin: string[]; }>; -export type Q_BooleanComparisonValue = boolean; -export type Q_DateComparisonValue = Q_NumberComparisonValue; -export type Q_EnumComparisonValue = E | OneOf<{ +export declare type Q_BooleanComparisonValue = boolean; +export declare type Q_DateComparisonValue = Q_NumberComparisonValue; +export declare type Q_EnumComparisonValue = E | OneOf<{ $in: E[]; $nin: E[]; }>; -export type Q_ExistsValue = { +export declare type Q_ExistsValue = { $exists: boolean; }; -export type Q_NumberValue = Q_NumberComparisonValue | Q_ExistsValue; -export type Q_StringValue = Q_StringComparisonValue | Q_ExistsValue; -export type Q_BooleanValue = Q_BooleanComparisonValue | Q_ExistsValue; -export type Q_DateValue = Q_DateComparisonValue | Q_ExistsValue; -export type Q_EnumValue = Q_EnumComparisonValue | Q_ExistsValue; -export type Q_State = S | { +export declare type Q_NumberValue = Q_NumberComparisonValue | Q_ExistsValue; +export declare type Q_StringValue = Q_StringComparisonValue | Q_ExistsValue; +export declare type Q_BooleanValue = Q_BooleanComparisonValue | Q_ExistsValue; +export declare type Q_DateValue = Q_DateComparisonValue | Q_ExistsValue; +export declare type Q_EnumValue = Q_EnumComparisonValue | Q_ExistsValue; +export declare type Q_State = S | { $in: S[]; } | { $nin: S[]; } | Q_ExistsValue; -export type Q_FullTextValue = { +export declare type Q_FullTextValue = { $search: string; $language?: 'zh_CN' | 'en_US'; }; -export type Q_FullTextKey = '$text'; -export type FulltextFilter = { +export declare type Q_FullTextKey = '$text'; +export declare type FulltextFilter = { [F in Q_FullTextKey]?: Q_FullTextValue; }; -type Q_LogicKey = '$and' | '$or'; -type Q_LinearLogicKey = '$not'; -export type MakeFilterWrapper = { +declare type Q_LogicKey = '$and' | '$or'; +declare type Q_LinearLogicKey = '$not'; +export declare type MakeFilterWrapper = { [Q in Q_LogicKey]?: Array>; } & { [Q in Q_LinearLogicKey]?: MakeFilterWrapper; } & Partial; -export type MakeFilter = { +export declare type MakeFilter = { '#id'?: NodeId; } & MakeFilterWrapper; -export type RefAttr = { +export declare type RefAttr = { '#attr': A; } | { '#refId': NodeId; diff --git a/lib/types/Endpoint.d.ts b/lib/types/Endpoint.d.ts index fa5415e..42e425b 100644 --- a/lib/types/Endpoint.d.ts +++ b/lib/types/Endpoint.d.ts @@ -8,4 +8,4 @@ export interface EndpointItem, headers: IncomingHttpHeaders, req: IncomingMessage, body?: any) => Promise; } -export type Endpoint> = EndpointItem | EndpointItem[]; +export declare type Endpoint> = EndpointItem | EndpointItem[]; diff --git a/lib/types/Entity.d.ts b/lib/types/Entity.d.ts index 46b264a..9e71be5 100644 --- a/lib/types/Entity.d.ts +++ b/lib/types/Entity.d.ts @@ -1,11 +1,12 @@ +import { ReadOnlyAction } from '../actions/action'; import { PrimaryKey, Sequence } from './DataType'; -type TriggerDataAttributeType = '$$triggerData$$'; -type TriggerTimestampAttributeType = '$$triggerTimestamp$$'; -type PrimaryKeyAttributeType = 'id'; -type CreateAtAttributeType = '$$createAt$$'; -type UpdateAtAttributeType = '$$updateAt$$'; -type DeleteAtAttributeType = '$$deleteAt$$'; -type SeqAttributeType = '$$seq$$'; +declare type TriggerDataAttributeType = '$$triggerData$$'; +declare type TriggerTimestampAttributeType = '$$triggerTimestamp$$'; +declare type PrimaryKeyAttributeType = 'id'; +declare type CreateAtAttributeType = '$$createAt$$'; +declare type UpdateAtAttributeType = '$$updateAt$$'; +declare type DeleteAtAttributeType = '$$deleteAt$$'; +declare type SeqAttributeType = '$$seq$$'; export declare const TriggerDataAttribute = "$$triggerData$$"; export declare const TriggerTimestampAttribute = "$$triggerTimestamp$$"; export declare const PrimaryKeyAttribute = "id"; @@ -13,14 +14,14 @@ export declare const CreateAtAttribute = "$$createAt$$"; export declare const UpdateAtAttribute = "$$updateAt$$"; export declare const DeleteAtAttribute = "$$deleteAt$$"; export declare const SeqAttribute = "$$seq$$"; -export type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType | DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType; +export declare type InstinctiveAttributes = PrimaryKeyAttributeType | CreateAtAttributeType | UpdateAtAttributeType | DeleteAtAttributeType | TriggerDataAttributeType | TriggerTimestampAttributeType | SeqAttributeType; export declare const initinctiveAttributes: string[]; -type FilterPart = { +declare type FilterPart = { filter?: A extends 'create' ? undefined : F; indexFrom?: A extends 'create' ? undefined : number; count?: A extends 'create' ? undefined : number; }; -export type SelectOption = { +export declare type SelectOption = { dontCollect?: boolean; blockTrigger?: true; obscure?: boolean; @@ -28,7 +29,7 @@ export type SelectOption = { includedDeleted?: true; dummy?: 1; }; -export type OperateOption = { +export declare type OperateOption = { blockTrigger?: true; dontCollect?: boolean; dontCreateOper?: boolean; @@ -38,13 +39,19 @@ export type OperateOption = { modiParentEntity?: string; dummy?: 1; }; -export type FormUpdateData = Partial<{ +export declare type FormUpdateData = Partial<{ [K in keyof Omit]: SH[K] | null; }>; -export type FormCreateData = Partial> & { +export declare type FormCreateData = Partial> & { id: string; }; -export type Operation = { +export declare type Operation = { + id: string; + action: A; + data: D; + sorter?: S; +} & FilterPart; +export declare type Selection = { id?: string; action: A; data: D; @@ -60,14 +67,14 @@ export interface EntityShape { interface GeneralEntityShape extends EntityShape { [K: string]: any; } -export type MakeAction = A; +export declare type MakeAction = A; export interface EntityDef { Schema: GeneralEntityShape; OpSchema: GeneralEntityShape; Action: string; ParticularAction?: string; - Selection: Omit, 'action'>; - Aggregation: Omit, 'action'>; + Selection: Omit, 'action'>; + Aggregation: DeduceAggregation; Operation: CUDOperation; Create: CreateOperation; CreateSingle: CreateSingleOperation; @@ -82,79 +89,79 @@ export interface EntityDict { export interface OtmSubProjection extends Omit, 'action'> { $entity: string; } -export type AggregationOp = `#max-${number}` | `#min-${number}` | `#avg-${number}` | `#count-${number}` | `#sum-${number}`; -export type DeduceAggregationData

= { +export declare type AggregationOp = `#max-${number}` | `#min-${number}` | `#avg-${number}` | `#count-${number}` | `#sum-${number}`; +export declare type DeduceAggregationData

= { [A in AggregationOp]?: P; } & { '#aggr'?: P; }; -export type AggregationResult = Array<{ +export declare type AggregationResult = Array<{ [A in AggregationOp]?: number | string; } & { '#data'?: Partial; }>; -export type AttrFilter = { +export declare type AttrFilter = { [K in keyof SH]?: any; }; -type SortAttr = { +declare type SortAttr = { [K: string]: any; }; -type SorterItem = { +declare type SorterItem = { $attr: SortAttr; $direction?: "asc" | "desc"; }; -type Sorter = Array; -type Filter = { +declare type Sorter = Array; +declare type Filter = { [K: string]: any; }; -type Projection = { +declare type Projection = { [K: string]: any; }; -export type DeduceAggregation

= Omit, F, S>, 'action'>; -type CreateOperationData = { +export declare type DeduceAggregation

= Omit, F, S>, 'action'>; +declare type CreateOperationData = { id: string; [K: string]: any; }; -type CreateSingleOperation = Operation<'create', CreateOperationData, undefined, undefined>; -type CreateMultipleOperation = Operation<'create', Array, undefined, undefined>; -type CreateOperation = CreateSingleOperation | CreateMultipleOperation; -type UpdateOperationData = { +declare type CreateSingleOperation = Operation<'create', CreateOperationData, undefined, undefined>; +declare type CreateMultipleOperation = Operation<'create', Array, undefined, undefined>; +declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation; +declare type UpdateOperationData = { id?: never; [k: string]: any; }; -export type UpdateOperation = Operation; -type RemoveOperationData = { +export declare type UpdateOperation = Operation; +declare type RemoveOperationData = { [k: string]: any; }; -export type RemoveOperation = Operation<'remove', RemoveOperationData, Filter, Sorter>; -export type CUDOperation = CreateOperation | UpdateOperation | RemoveOperation; -export type CreateOpResult = { +export declare type RemoveOperation = Operation<'remove', RemoveOperationData, Filter, Sorter>; +export declare type CUDOperation = CreateOperation | UpdateOperation | RemoveOperation; +export declare type CreateOpResult = { a: 'c'; e: T; d: ED[T]['OpSchema'] | ED[T]['OpSchema'][]; }; -export type UpdateOpResult = { +export declare type UpdateOpResult = { a: 'u'; e: T; d: UpdateOperationData; f?: Filter; }; -export type RemoveOpResult = { +export declare type RemoveOpResult = { a: 'r'; e: T; f?: Filter; }; -export type RelationHierarchy = { +export declare type RelationHierarchy = { [K in R]?: R[]; }; -export type CascadeRelationItem = { +export declare type CascadeRelationItem = { cascadePath: string; relations?: string[]; }; -export type CascadeRelationAuth = { +export declare type CascadeRelationAuth = { [K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; }; -export type SelectOpResult = { +export declare type SelectOpResult = { a: 's'; d: { [T in keyof ED]?: { @@ -162,14 +169,14 @@ export type SelectOpResult = { }; }; }; -export type OpRecord = CreateOpResult | UpdateOpResult | RemoveOpResult | SelectOpResult; -export type OperationResult = { +export declare type OpRecord = CreateOpResult | UpdateOpResult | RemoveOpResult | SelectOpResult; +export declare type OperationResult = { [K in keyof ED]?: { [A in ED[K]['Action']]?: number; }; }; -export type ActionType = 'readOnly' | 'appendOnly' | 'excludeUpdate' | 'excludeRemove' | 'crud'; -export type Configuration = { +export declare type ActionType = 'readOnly' | 'appendOnly' | 'excludeUpdate' | 'excludeRemove' | 'crud'; +export declare type Configuration = { actionType?: ActionType; static?: boolean; }; diff --git a/lib/types/Exception.d.ts b/lib/types/Exception.d.ts index 13bdcb9..80948c5 100644 --- a/lib/types/Exception.d.ts +++ b/lib/types/Exception.d.ts @@ -5,6 +5,16 @@ export declare class OakException extends Error { } export declare class OakDataException extends OakException { } +export declare class OakUniqueViolationException extends OakException { + rows: Array<{ + id?: string; + attrs: string[]; + }>; + constructor(rows: Array<{ + id?: string; + attrs: string[]; + }>, message?: string); +} export declare class OakImportDataParseException extends OakException { line: number; header?: string; diff --git a/lib/types/Exception.js b/lib/types/Exception.js index 33d3e27..f8d492d 100644 --- a/lib/types/Exception.js +++ b/lib/types/Exception.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.makeException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakDataException = exports.OakException = void 0; +exports.makeException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0; var tslib_1 = require("tslib"); var OakException = /** @class */ (function (_super) { tslib_1.__extends(OakException, _super); @@ -36,6 +36,16 @@ var OakDataException = /** @class */ (function (_super) { return OakDataException; }(OakException)); exports.OakDataException = OakDataException; +var OakUniqueViolationException = /** @class */ (function (_super) { + tslib_1.__extends(OakUniqueViolationException, _super); + function OakUniqueViolationException(rows, message) { + var _this = _super.call(this, message || '您更新的数据违反了唯一性约束') || this; + _this.rows = rows; + return _this; + } + return OakUniqueViolationException; +}(OakException)); +exports.OakUniqueViolationException = OakUniqueViolationException; var OakImportDataParseException = /** @class */ (function (_super) { tslib_1.__extends(OakImportDataParseException, _super); // message必传,描述具体错误的数据内容 @@ -257,6 +267,12 @@ function makeException(data) { case 'OakDeadlock': { return new OakDeadlock(data.message); } + case 'OakDataException': { + return new OakDataException(data.message); + } + case 'OakUniqueViolationException': { + return new OakUniqueViolationException(data.rows, data.message); + } case 'OakImportDataParseException': { return new OakImportDataParseException(data.message, data.line, data.header); } diff --git a/lib/types/Expression.d.ts b/lib/types/Expression.d.ts index a379eb8..cdeb126 100644 --- a/lib/types/Expression.d.ts +++ b/lib/types/Expression.d.ts @@ -1,8 +1,8 @@ import { RefAttr } from "./Demand"; import { Geo } from "./Geo"; -export type RefOrExpression = RefAttr | Expression; -type MathType = RefOrExpression | number; -type StringType = RefOrExpression | string; +export declare type RefOrExpression = RefAttr | Expression; +declare type MathType = RefOrExpression | number; +declare type StringType = RefOrExpression | string; interface Add { $add: (MathType)[]; } @@ -30,8 +30,8 @@ interface Ceil { interface Pow { $pow: [MathType, MathType]; } -type MathExpression = Add | Subtract | Multiply | Divide | Abs | Round | Floor | Ceil | Pow; -type CmpType = RefOrExpression | string | number; +declare type MathExpression = Add | Subtract | Multiply | Divide | Abs | Round | Floor | Ceil | Pow; +declare type CmpType = RefOrExpression | string | number; interface Gt { $gt: [CmpType, CmpType]; } @@ -59,14 +59,14 @@ interface EndsWith { interface Includes { $includes: [RefOrExpression | string, RefOrExpression | string]; } -type CompareExpression = Lt | Gt | Lte | Gte | Eq | Ne | StartsWith | EndsWith | Includes; +declare type CompareExpression = Lt | Gt | Lte | Gte | Eq | Ne | StartsWith | EndsWith | Includes; interface BoolTrue { $true: Expression; } interface BoolFalse { $false: Expression; } -type BoolExpression = BoolTrue | BoolFalse; +declare type BoolExpression = BoolTrue | BoolFalse; interface LogicAnd { $and: Expression[]; } @@ -76,7 +76,7 @@ interface LogicOr { interface LogicNot { $not: Expression; } -type LogicExpression = LogicAnd | LogicOr | LogicNot; +declare type LogicExpression = LogicAnd | LogicOr | LogicNot; interface DateYear { $year: RefOrExpression | Date | number; } @@ -110,18 +110,18 @@ interface DateCeiling { interface DateFloor { $dateFloor: [RefOrExpression | Date | number, 'y' | 'M' | 'd' | 'h' | 'm' | 's']; } -type DateExpression = DateYear | DateMonth | DateWeekday | DateWeekOfYear | DateDay | DateDayOfYear | DateDayOfMonth | DateDayOfWeek | DateDiff | DateCeiling | DateFloor; +declare type DateExpression = DateYear | DateMonth | DateWeekday | DateWeekOfYear | DateDay | DateDayOfYear | DateDayOfMonth | DateDayOfWeek | DateDiff | DateCeiling | DateFloor; interface StringConcat { $concat: StringType[]; } -type StringExpression = StringConcat; +declare type StringExpression = StringConcat; interface GeoContains { $contains: [RefOrExpression | Geo, RefOrExpression | Geo]; } interface GeoDistance { $distance: [RefOrExpression | Geo, RefOrExpression | Geo]; } -type GeoExpression = GeoContains | GeoDistance; +declare type GeoExpression = GeoContains | GeoDistance; interface AggrCountExpression { $$count: RefOrExpression; } @@ -137,9 +137,9 @@ interface AggrMinExpression { interface AggrAvgExpression { $$avg: RefOrExpression; } -export type AggrExpression = AggrAvgExpression | AggrCountExpression | AggrSumExpression | AggrMaxExpression | AggrMinExpression; -export type Expression = GeoExpression | DateExpression | LogicExpression | BoolExpression | CompareExpression | MathExpression | StringExpression | AggrExpression; -export type ExpressionConstant = Geo | number | Date | string | boolean; +export declare type AggrExpression = AggrAvgExpression | AggrCountExpression | AggrSumExpression | AggrMaxExpression | AggrMinExpression; +export declare type Expression = GeoExpression | DateExpression | LogicExpression | BoolExpression | CompareExpression | MathExpression | StringExpression | AggrExpression; +export declare type ExpressionConstant = Geo | number | Date | string | boolean; export declare function isGeoExpression(expression: any): expression is GeoExpression; export declare function isDateExpression(expression: any): expression is DateExpression; export declare function isLogicExpression(expression: any): expression is LogicExpression; diff --git a/lib/types/Geo.d.ts b/lib/types/Geo.d.ts index f1ce57e..1747c3f 100644 --- a/lib/types/Geo.d.ts +++ b/lib/types/Geo.d.ts @@ -1,8 +1,8 @@ -export type Point = [number, number]; -export type Path = Array; -export type Polygon = Array; -export type Circle = [Point, number]; -export type SingleGeo = { +export declare type Point = [number, number]; +export declare type Path = Array; +export declare type Polygon = Array; +export declare type Circle = [Point, number]; +export declare type SingleGeo = { type: 'point'; coordinate: Point; } | { @@ -15,4 +15,4 @@ export type SingleGeo = { type: 'circle'; coordinate: Circle; }; -export type Geo = SingleGeo | SingleGeo[]; +export declare type Geo = SingleGeo | SingleGeo[]; diff --git a/lib/types/Locale.d.ts b/lib/types/Locale.d.ts index 190a966..108740e 100644 --- a/lib/types/Locale.d.ts +++ b/lib/types/Locale.d.ts @@ -1,17 +1,17 @@ import { EntityShape } from "./Entity"; -type Language = 'zh_CN' | 'en_US'; -type LocaleOfSchema> = { +declare type Language = 'zh_CN' | 'en_US'; +declare type LocaleOfSchema> = { [A in keyof Required>]: string; }; -type LocaleOfStringEnum = { +declare type LocaleOfStringEnum = { [K in A]: string; }; -type LocaleOfValue> = { +declare type LocaleOfValue> = { [K in keyof V]: { [K2 in V[K]]: string; }; }; -export type LocaleDef, Ac extends string, R extends string, V extends Record> = { +export declare type LocaleDef, Ac extends string, R extends string, V extends Record> = { [L in Language]?: { attr: LocaleOfSchema & { [A in keyof V]: string; diff --git a/lib/types/Polyfill.d.ts b/lib/types/Polyfill.d.ts index f096aaa..7a2dfd8 100644 --- a/lib/types/Polyfill.d.ts +++ b/lib/types/Polyfill.d.ts @@ -1,23 +1,23 @@ -export type OmitInferKey = { +export declare type OmitInferKey = { [K in keyof T as T extends R ? never : K]: T[K]; }; -export type OmitInferValue = { +export declare type OmitInferValue = { [K in keyof T as T extends R ? never : K]: T[K]; }; -export type ValueOf = Obj[keyof Obj]; -export type OneOnly = { +export declare type ValueOf = Obj[keyof Obj]; +export declare type OneOnly = { [key in Exclude]?: undefined; } & Pick; -export type OneOfByKey = { +export declare type OneOfByKey = { [key in keyof Obj]: OneOnly; }; -export type OneOf = ValueOf>; -type IsOptional = { +export declare type OneOf = ValueOf>; +declare type IsOptional = { [K1 in Exclude]: T[K1]; } & { K?: T[K]; } extends T ? K : never; -export type OptionalKeys = { +export declare type OptionalKeys = { [K in keyof T]: IsOptional; }[keyof T]; export {}; diff --git a/lib/types/Port.d.ts b/lib/types/Port.d.ts index 3477304..8e0519a 100644 --- a/lib/types/Port.d.ts +++ b/lib/types/Port.d.ts @@ -1,6 +1,6 @@ import { AsyncContext } from "../store/AsyncRowStore"; import { EntityDict } from "./Entity"; -export type Exportation = { +export declare type Exportation = { name: string; id: string; entity: T; @@ -8,7 +8,7 @@ export type Exportation Partial>; }; -export type Importation = { +export declare type Importation = { name: string; id: string; entity: T; diff --git a/lib/types/RowStore.d.ts b/lib/types/RowStore.d.ts index be4bec2..8c632f0 100644 --- a/lib/types/RowStore.d.ts +++ b/lib/types/RowStore.d.ts @@ -1,6 +1,6 @@ import { OperationResult, EntityDict } from './Entity'; import { StorageSchema } from './Storage'; -export type TxnOption = { +export declare type TxnOption = { isolationLevel: 'repeatable read' | 'serializable'; }; export declare abstract class RowStore { diff --git a/lib/types/Storage.d.ts b/lib/types/Storage.d.ts index 99357b4..341fabc 100644 --- a/lib/types/Storage.d.ts +++ b/lib/types/Storage.d.ts @@ -1,7 +1,7 @@ import { ActionType } from '.'; import { EntityDict, EntityShape, InstinctiveAttributes } from './Entity'; import { DataType, DataTypeParams } from './schema/DataTypes'; -export type Ref = 'ref'; +export declare type Ref = 'ref'; export interface Column { name: keyof SH | `${string}State`; size?: number; @@ -27,12 +27,12 @@ export interface Attribute { unique?: boolean; sequenceStart?: number; } -export type Attributes = Omit<{ +export declare type Attributes = Omit<{ [attrName in keyof SH]: Attribute; }, InstinctiveAttributes>; export interface EntityConfig { } -export type UniqConstraint = { +export declare type UniqConstraint = { attributes: Array; type?: string; }; @@ -51,6 +51,6 @@ export interface StorageDesc { relation?: string[]; view?: true; } -export type StorageSchema = { +export declare type StorageSchema = { [K in keyof ED]: StorageDesc; }; diff --git a/lib/types/Timer.d.ts b/lib/types/Timer.d.ts index ede68e0..c3d869a 100644 --- a/lib/types/Timer.d.ts +++ b/lib/types/Timer.d.ts @@ -1,11 +1,11 @@ import { EntityDict } from './Entity'; import { AsyncContext } from "../store/AsyncRowStore"; -type RoutineFn> = (context: Cxt) => Promise; -export type Routine> = { +declare type RoutineFn> = (context: Cxt) => Promise; +export declare type Routine> = { name: string; fn: RoutineFn; }; -export type Timer> = { +export declare type Timer> = { name: string; cron: string; fn: RoutineFn; diff --git a/lib/types/Trigger.d.ts b/lib/types/Trigger.d.ts index 4a0f0e6..0015048 100644 --- a/lib/types/Trigger.d.ts +++ b/lib/types/Trigger.d.ts @@ -24,7 +24,7 @@ export interface CreateTriggerCrossTxn | SyncContext> = CreateTriggerInTxn | CreateTriggerCrossTxn; +export declare type CreateTrigger | SyncContext> = CreateTriggerInTxn | CreateTriggerCrossTxn; /** * update trigger如果带有filter,说明只对存在限定条件的行起作用。此时系统在进行相应动作时, * 会判定当前动作的filter条件和trigger所定义的filter是否有交集(即有同时满足两个条件的行) @@ -46,7 +46,7 @@ export interface UpdateTriggerCrossTxn | SyncContext> = UpdateTriggerInTxn | UpdateTriggerCrossTxn; +export declare type UpdateTrigger | SyncContext> = UpdateTriggerInTxn | UpdateTriggerCrossTxn; /** * 同update trigger一样,remove trigger如果带有filter,说明只对存在限定条件的行起作用。此时系统在进行相应动作时, * 会判定当前动作的filter条件和trigger所定义的filter是否有交集(即有同时满足两个条件的行) @@ -67,7 +67,7 @@ export interface RemoveTriggerCrossTxn | SyncContext> = RemoveTriggerInTxn | RemoveTriggerCrossTxn; +export declare type RemoveTrigger | SyncContext> = RemoveTriggerInTxn | RemoveTriggerCrossTxn; export interface SelectTriggerBase extends TriggerBase { action: 'select'; } @@ -88,8 +88,8 @@ export interface SelectTriggerAfter[]; }, context: Cxt, params?: SelectOption) => Promise | number; } -export type SelectTrigger | SyncContext> = SelectTriggerBefore | SelectTriggerAfter; -export type Trigger | SyncContext> = CreateTrigger | UpdateTrigger | RemoveTrigger | SelectTrigger; +export declare type SelectTrigger | SyncContext> = SelectTriggerBefore | SelectTriggerAfter; +export declare type Trigger | SyncContext> = CreateTrigger | UpdateTrigger | RemoveTrigger | SelectTrigger; export interface TriggerEntityShape extends EntityShape { $$triggerData$$?: { name: string; diff --git a/lib/types/Watcher.d.ts b/lib/types/Watcher.d.ts index 0fe8135..fccbd9c 100644 --- a/lib/types/Watcher.d.ts +++ b/lib/types/Watcher.d.ts @@ -1,6 +1,6 @@ import { AsyncContext } from "../store/AsyncRowStore"; import { EntityDict, OperationResult } from "./Entity"; -type ActionData = ED[T]['Update']['data'] | ED[T]['Remove']['data']; +declare type ActionData = ED[T]['Update']['data'] | ED[T]['Remove']['data']; export interface BBWatcher { name: string; entity: T; @@ -15,5 +15,5 @@ export interface WBWatcher Promise); fn: (context: Cxt, data: Partial[]) => Promise>; } -export type Watcher> = BBWatcher | WBWatcher; +export declare type Watcher> = BBWatcher | WBWatcher; export {}; diff --git a/lib/types/schema/DataTypes.d.ts b/lib/types/schema/DataTypes.d.ts index fbc2e69..e3c9fba 100644 --- a/lib/types/schema/DataTypes.d.ts +++ b/lib/types/schema/DataTypes.d.ts @@ -1,28 +1,28 @@ /** * Column types used for @PrimaryGeneratedColumn() decorator. */ -export type PrimaryGeneratedColumnType = "int" | "int2" | "int4" | "int8" | "integer" | "tinyint" | "smallint" | "mediumint" | "bigint" | "dec" | "decimal" | "smalldecimal" | "fixed" | "numeric" | "number"; +export declare type PrimaryGeneratedColumnType = "int" | "int2" | "int4" | "int8" | "integer" | "tinyint" | "smallint" | "mediumint" | "bigint" | "dec" | "decimal" | "smalldecimal" | "fixed" | "numeric" | "number"; /** * Column types where spatial properties are used. */ -export type SpatialColumnType = "geometry" | "geography" | "st_geometry" | "st_point"; +export declare type SpatialColumnType = "geometry" | "geography" | "st_geometry" | "st_point"; /** * Column types where precision and scale properties are used. */ -export type WithPrecisionColumnType = "float" | "double" | "dec" | "decimal" | "smalldecimal" | "fixed" | "numeric" | "real" | "double precision" | "number" | "datetime" | "datetime2" | "datetimeoffset" | "time" | "time with time zone" | "time without time zone" | "timestamp" | "timestamp without time zone" | "timestamp with time zone" | "timestamp with local time zone"; +export declare type WithPrecisionColumnType = "float" | "double" | "dec" | "decimal" | "smalldecimal" | "fixed" | "numeric" | "real" | "double precision" | "number" | "datetime" | "datetime2" | "datetimeoffset" | "time" | "time with time zone" | "time without time zone" | "timestamp" | "timestamp without time zone" | "timestamp with time zone" | "timestamp with local time zone"; /** * Column types where column length is used. */ -export type WithLengthColumnType = "character varying" | "varying character" | "char varying" | "nvarchar" | "national varchar" | "character" | "native character" | "varchar" | "char" | "nchar" | "national char" | "varchar2" | "nvarchar2" | "alphanum" | "shorttext" | "raw" | "binary" | "varbinary" | "string"; -export type WithWidthColumnType = "tinyint" | "smallint" | "mediumint" | "int" | "bigint"; +export declare type WithLengthColumnType = "character varying" | "varying character" | "char varying" | "nvarchar" | "national varchar" | "character" | "native character" | "varchar" | "char" | "nchar" | "national char" | "varchar2" | "nvarchar2" | "alphanum" | "shorttext" | "raw" | "binary" | "varbinary" | "string"; +export declare type WithWidthColumnType = "tinyint" | "smallint" | "mediumint" | "int" | "bigint"; /** * All other regular column types. */ -export type SimpleColumnType = "simple-array" | "simple-json" | "simple-enum" | "int2" | "integer" | "int4" | "int8" | "int64" | "unsigned big int" | "float" | "float4" | "float8" | "smallmoney" | "money" | "boolean" | "bool" | "tinyblob" | "tinytext" | "mediumblob" | "mediumtext" | "blob" | "text" | "ntext" | "citext" | "hstore" | "longblob" | "longtext" | "alphanum" | "shorttext" | "bytes" | "bytea" | "long" | "raw" | "long raw" | "bfile" | "clob" | "nclob" | "image" | "timetz" | "timestamptz" | "timestamp with local time zone" | "smalldatetime" | "date" | "interval year to month" | "interval day to second" | "interval" | "year" | "seconddate" | "point" | "line" | "lseg" | "box" | "circle" | "path" | "polygon" | "geography" | "geometry" | "linestring" | "multipoint" | "multilinestring" | "multipolygon" | "geometrycollection" | "st_geometry" | "st_point" | "int4range" | "int8range" | "numrange" | "tsrange" | "tstzrange" | "daterange" | "enum" | "set" | "cidr" | "inet" | "macaddr" | "bit" | "bit varying" | "varbit" | "tsvector" | "tsquery" | "uuid" | "xml" | "json" | "jsonb" | "varbinary" | "hierarchyid" | "sql_variant" | "rowid" | "urowid" | "uniqueidentifier" | "rowversion" | "array" | "cube" | "ltree" | "object" | "array" | "function" | "sequence"; +export declare type SimpleColumnType = "simple-array" | "simple-json" | "simple-enum" | "int2" | "integer" | "int4" | "int8" | "int64" | "unsigned big int" | "float" | "float4" | "float8" | "smallmoney" | "money" | "boolean" | "bool" | "tinyblob" | "tinytext" | "mediumblob" | "mediumtext" | "blob" | "text" | "ntext" | "citext" | "hstore" | "longblob" | "longtext" | "alphanum" | "shorttext" | "bytes" | "bytea" | "long" | "raw" | "long raw" | "bfile" | "clob" | "nclob" | "image" | "timetz" | "timestamptz" | "timestamp with local time zone" | "smalldatetime" | "date" | "interval year to month" | "interval day to second" | "interval" | "year" | "seconddate" | "point" | "line" | "lseg" | "box" | "circle" | "path" | "polygon" | "geography" | "geometry" | "linestring" | "multipoint" | "multilinestring" | "multipolygon" | "geometrycollection" | "st_geometry" | "st_point" | "int4range" | "int8range" | "numrange" | "tsrange" | "tstzrange" | "daterange" | "enum" | "set" | "cidr" | "inet" | "macaddr" | "bit" | "bit varying" | "varbit" | "tsvector" | "tsquery" | "uuid" | "xml" | "json" | "jsonb" | "varbinary" | "hierarchyid" | "sql_variant" | "rowid" | "urowid" | "uniqueidentifier" | "rowversion" | "array" | "cube" | "ltree" | "object" | "array" | "function" | "sequence"; /** * Any column type column can be. */ -export type DataType = WithPrecisionColumnType | WithLengthColumnType | WithWidthColumnType | SpatialColumnType | SimpleColumnType; +export declare type DataType = WithPrecisionColumnType | WithLengthColumnType | WithWidthColumnType | SpatialColumnType | SimpleColumnType; export interface DataTypeParams { length?: number; width?: number; diff --git a/lib/utils/concurrent.d.ts b/lib/utils/concurrent.d.ts index 57d929b..c44feca 100644 --- a/lib/utils/concurrent.d.ts +++ b/lib/utils/concurrent.d.ts @@ -1,4 +1,4 @@ -type Mode = 'S' | 'X'; +declare type Mode = 'S' | 'X'; /** * 模拟一个读写锁,用于同步。 * 注意,千万不要发生自己等自己 diff --git a/lib/utils/uuid.d.ts b/lib/utils/uuid.d.ts index 595afb7..1d7ed56 100644 --- a/lib/utils/uuid.d.ts +++ b/lib/utils/uuid.d.ts @@ -3,7 +3,7 @@ export declare function sequentialUuid({ random }: { }): string; export declare function shrinkUuidTo32Bytes(uuid: string): string; export declare function expandUuidTo36Bytes(uuidShrinked: string): string; -export type GenerateIdOption = { +export declare type GenerateIdOption = { shuffle?: boolean; }; export declare function generateNewIdAsync(option?: GenerateIdOption): Promise; diff --git a/lib/utils/uuid.js b/lib/utils/uuid.js index 712a51e..0733d00 100644 --- a/lib/utils/uuid.js +++ b/lib/utils/uuid.js @@ -105,7 +105,7 @@ function sequentialUuid(_a) { } exports.sequentialUuid = sequentialUuid; function shrinkUuidTo32Bytes(uuid) { - return uuid.replaceAll('-', ''); + return uuid.replace(/\-/g, ''); } exports.shrinkUuidTo32Bytes = shrinkUuidTo32Bytes; function expandUuidTo36Bytes(uuidShrinked) { diff --git a/lib/utils/validator.d.ts b/lib/utils/validator.d.ts index bfa8afd..318c121 100644 --- a/lib/utils/validator.d.ts +++ b/lib/utils/validator.d.ts @@ -1,5 +1,5 @@ -type ValidatorFunction = (text: string, size?: number) => string | boolean; -type ValidatorMoneyFunction = (text: string, zero?: boolean) => string | boolean; +declare type ValidatorFunction = (text: string, size?: number) => string | boolean; +declare type ValidatorMoneyFunction = (text: string, zero?: boolean) => string | boolean; export declare const isMobile: ValidatorFunction; export declare const isPassword: ValidatorFunction; export declare const isCaptcha: ValidatorFunction; diff --git a/package.json b/package.json index e94c44b..7d04d7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oak-domain", - "version": "2.4.1", + "version": "2.4.2", "author": { "name": "XuChang" }, diff --git a/src/actions/action.ts b/src/actions/action.ts index 030cb75..84c701f 100644 --- a/src/actions/action.ts +++ b/src/actions/action.ts @@ -1,12 +1,12 @@ import { ActionDef } from '../types/Action'; -export type ReadOnlyAction = 'select' | 'count' | 'stat' | 'download'; +export type ReadOnlyAction = 'select' | 'count' | 'stat' | 'download' | 'aggregate'; export type AppendOnlyAction = ReadOnlyAction | 'create'; export type ExcludeUpdateAction = AppendOnlyAction | 'remove'; export type ExcludeRemoveAction = AppendOnlyAction | 'update'; export type GenericAction = 'update' | ExcludeUpdateAction; export type RelationAction = 'grant' | 'revoke'; -export const readOnlyActions = ['count', 'stat', 'download', 'select']; +export const readOnlyActions = ['count', 'stat', 'download', 'select', 'aggregate']; export const appendOnlyActions = readOnlyActions.concat('create'); export const excludeUpdateActions = appendOnlyActions.concat('remove'); export const exludeRemoveActions = appendOnlyActions.concat('update'); diff --git a/src/compiler/schemalBuilder.ts b/src/compiler/schemalBuilder.ts index 4d4f572..cc11e30 100644 --- a/src/compiler/schemalBuilder.ts +++ b/src/compiler/schemalBuilder.ts @@ -1,9 +1,8 @@ import PathLib from 'path'; import assert from 'assert'; -import { execSync } from 'child_process'; import { writeFileSync, readdirSync, mkdirSync, fstat } from 'fs'; import { emptydirSync } from 'fs-extra'; -import { assign, cloneDeep, difference, identity, intersection, keys, uniq, uniqBy } from 'lodash'; +import { assign, cloneDeep, difference, identity, intersection, keys, pull, uniq, uniqBy } from 'lodash'; import * as ts from 'typescript'; const { factory } = ts; import { @@ -952,12 +951,12 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela } ) as ts.PropertyAssignment; assert(ts.isStringLiteral(nameProperty.initializer)); - const indexName = nameProperty.initializer.text; - if (indexNameDict[indexName]) { - throw new Error(`「${filename}」索引定义重名「${indexName}」`); + const nameText = nameProperty.initializer.text; + if (indexNameDict[nameText]) { + throw new Error(`「${filename}」索引定义重名「${nameText}」`); } assign(indexNameDict, { - [indexName]: true, + [nameText]: true, }); const configProperty = properties.find( @@ -1010,11 +1009,11 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela } ) as ts.PropertySignature; if (!schemaNode) { - throw new Error(`「${filename}」中索引「${indexName}」的属性「${indexAttrName}」定义非法`); + throw new Error(`「${filename}」中索引「${nameText}」的属性「${indexAttrName}」定义非法`); } const { type, name } = schemaNode; - const entity = firstLetterLowerCase(moduleName); + const entity = moduleName; const { [entity]: manyToOneSet } = ManyToOne; if (ts.isTypeReferenceNode(type!)) { const { typeName } = type; @@ -1027,14 +1026,19 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela if (!manyToOneItem) { // 如果不是外键,则不能是Text, File if (isFulltextIndex) { - assert(['Text', 'String'].includes(text2), `「${filename}」中全文索引「${indexName}」定义的属性「${indexAttrName}」类型非法,只能是Text/String`); + assert(['Text', 'String'].includes(text2), `「${filename}」中全文索引「${nameText}」定义的属性「${indexAttrName}」类型非法,只能是Text/String`); } else { - assert(!unIndexedTypes.includes(text2), `「${filename}」中索引「${indexName}」的属性「${indexAttrName}」的类型为「${text2}」,不可索引`); + assert(!unIndexedTypes.includes(text2), `「${filename}」中索引「${nameText}」的属性「${indexAttrName}」的类型为「${text2}」,不可索引`); } } else { - assert(!isFulltextIndex, `「${filename}」中全文索引「${indexName}」的属性「${indexAttrName}」类型非法,只能为Text/String`); + assert(!isFulltextIndex, `「${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型非法,只能为Text/String`); + // 在这里把外键加上Id,这样storageSchema才能正常通过 + // 这里的写法不太好,未来TS版本高了可能会有问题。by Xc 20230131 + Object.assign(nameProperty, { + initializer: factory.createStringLiteral(`${indexAttrName}Id`), + }); } } else { @@ -1042,8 +1046,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela } } else { - assert(!isFulltextIndex, `「${filename}」中全文索引「${indexName}」的属性「${indexAttrName}」类型只能为Text/String`); - assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}中索引「${indexName}」的属性${(name).text}有定义非法`); + assert(!isFulltextIndex, `「${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型只能为Text/String`); + assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}中索引「${nameText}」的属性${(name).text}有定义非法`); } } } @@ -1587,6 +1591,7 @@ function constructFilter(statements: Array, entity: string) { break; } case 'Int': + case 'Uint': case 'Float': case 'Double': { type2 = factory.createTypeReferenceNode( @@ -2814,27 +2819,21 @@ function constructActions(statements: Array, entity: string) { ) ], factory.createTypeReferenceNode( - factory.createIdentifier("Omit"), + factory.createIdentifier("OakSelection"), [ + factory.createLiteralTypeNode(factory.createStringLiteral("select")), factory.createTypeReferenceNode( - factory.createIdentifier("OakOperation"), - [ - factory.createLiteralTypeNode(factory.createStringLiteral("select")), - factory.createTypeReferenceNode( - factory.createIdentifier("P"), - undefined - ), - factory.createTypeReferenceNode( - factory.createIdentifier("Filter"), - undefined - ), - factory.createTypeReferenceNode( - factory.createIdentifier("Sorter"), - undefined - ) - ] + factory.createIdentifier("P"), + undefined ), - factory.createLiteralTypeNode(factory.createStringLiteral("id")) + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) ] ) ), @@ -2878,26 +2877,20 @@ function constructActions(statements: Array, entity: string) { factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode( - factory.createIdentifier("Omit"), + factory.createIdentifier("DeduceAggregation"), [ factory.createTypeReferenceNode( - factory.createIdentifier("DeduceAggregation"), - [ - factory.createTypeReferenceNode( - factory.createIdentifier("Projection"), - undefined - ), - factory.createTypeReferenceNode( - factory.createIdentifier("Filter"), - undefined - ), - factory.createTypeReferenceNode( - factory.createIdentifier("Sorter"), - undefined - ) - ] + factory.createIdentifier("Projection"), + undefined ), - factory.createLiteralTypeNode(factory.createStringLiteral("id")) + factory.createTypeReferenceNode( + factory.createIdentifier("Filter"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("Sorter"), + undefined + ) ] ) ) @@ -3775,7 +3768,7 @@ function constructActions(statements: Array, entity: string) { const entityNameLc = firstLetterLowerCase(entityName); foreignKeySet[entityName].forEach( (foreignKey) => { - const identifier = `${entityNameLc}s$${foreignKey}`; + const identifier = `${entityNameLc}$${foreignKey}`; const otmCreateOperationDataNode = factory.createTypeReferenceNode( factory.createIdentifier("Omit"), @@ -4340,6 +4333,11 @@ const initialStatements = () => [ undefined, factory.createIdentifier('Int') ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier('Uint') + ), factory.createImportSpecifier( false, undefined, @@ -4529,6 +4527,11 @@ const initialStatements = () => [ factory.createIdentifier("Operation"), factory.createIdentifier("OakOperation") ), + factory.createImportSpecifier( + false, + factory.createIdentifier("Selection"), + factory.createIdentifier("OakSelection") + ), factory.createImportSpecifier( false, factory.createIdentifier("MakeAction"), diff --git a/src/store/CascadeStore.ts b/src/store/CascadeStore.ts index 52a74cc..7c5ffee 100644 --- a/src/store/CascadeStore.ts +++ b/src/store/CascadeStore.ts @@ -669,7 +669,7 @@ export abstract class CascadeStore exten assert(relation instanceof Array); const [entityOtm, foreignKey] = relation; const otmOperations = data[attr]; - const dealWithOneToMany = (otm: ED[keyof ED]['Update']) => { + const dealWithOneToMany = (otm: ED[keyof ED]['Update'] | ED[keyof ED]['Create']) => { const { action: actionOtm, data: dataOtm, filter: filterOtm } = otm; if (!foreignKey) { // 基于entity/entityId的one-to-many @@ -692,7 +692,7 @@ export abstract class CascadeStore exten } else if (actionOtm === 'create') { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // todo 这个假设成立吗?等遇到create/create一对多的case再完善 + // todo 这个假设对watcher等后台行为可能不成立,等遇到create/create一对多的case再完善 const { id } = filter!; assert(typeof id === 'string'); if (dataOtm instanceof Array) { @@ -713,11 +713,9 @@ export abstract class CascadeStore exten else { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 // 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善 - const { id } = filter!; Object.assign(otm, { filter: addFilterSegment({ - entity, - entityId: id, + [entity]: filter, }, filterOtm), }); if (action === 'remove' && actionOtm === 'update') { @@ -747,7 +745,7 @@ export abstract class CascadeStore exten } else if (actionOtm === 'create') { // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // todo 这个假设成立吗?等遇到create/create一对多的case再完善 + // todo 这个假设在后台可能不成立,等遇到了再说 const { id } = filter!; assert(typeof id === 'string'); if (dataOtm instanceof Array) { @@ -764,12 +762,10 @@ export abstract class CascadeStore exten } } else { - // 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作 - // 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善 - const { id } = filter!; + // update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger) Object.assign(otm, { filter: addFilterSegment({ - [foreignKey]: id, + [foreignKey.slice(0, foreignKey.length - 2)]: filter, }, filterOtm), }); if (action === 'remove' && actionOtm === 'update') { @@ -780,7 +776,7 @@ export abstract class CascadeStore exten } } - afterFns.push(() => cascadeUpdate.call(this, entityOtm!, otm, context, option2)); + beforeFns.push(() => cascadeUpdate.call(this, entityOtm!, otm, context, option2)); }; if (otmOperations instanceof Array) { diff --git a/src/store/actionDef.ts b/src/store/actionDef.ts index 1c3d678..2605131 100644 --- a/src/store/actionDef.ts +++ b/src/store/actionDef.ts @@ -1,6 +1,8 @@ -import { ActionDictOfEntityDict, BBWatcher, Checker, EntityDict, StorageSchema, Trigger, RowChecker } from "../types"; +import { ActionDictOfEntityDict, BBWatcher, Checker, EntityDict, StorageSchema, Trigger, RowChecker, OakDataException, OakUniqueViolationException } from "../types"; import { SyncContext } from "./SyncRowStore"; import { AsyncContext } from "./AsyncRowStore"; +import { uniqBy, pick, intersection } from '../utils/lodash'; +import { addFilterSegment } from "./filter"; export function getFullProjection(entity: T, schema: StorageSchema) { const { attributes } = schema[entity]; @@ -24,18 +26,17 @@ function makeIntrinsicWatchers(schema: StorageSchema) for (const entity in schema) { const { attributes } = schema[entity]; - const now = Date.now(); const { expiresAt, expired } = attributes; if (expiresAt && expiresAt.type === 'datetime' && expired && expired.type === 'boolean') { // 如果有定义expiresAt和expired,则自动生成一个检查的watcher watchers.push({ entity, name: `对象${entity}上的过期自动watcher`, - filter: async () => { + filter: () => { return { expired: false, expiresAt: { - $lte: now, + $lte: Date.now(), }, }; }, @@ -50,9 +51,75 @@ function makeIntrinsicWatchers(schema: StorageSchema) return watchers; } +function checkUniqueBetweenRows(rows: Record[], uniqAttrs: string[]) { + // 先检查这些行本身之间有无unique冲突 + const uniqRows = uniqBy(rows, (d) => { + let s = ''; + for (const a of uniqAttrs) { + if (d[a as string] === null || d[a as string] === undefined) { + s + d.id; + } + else { + s + `-${d[a as string]}`; + } + } + return s; + }); + if (uniqRows.length < rows.length) { + throw new OakUniqueViolationException([{ + attrs: uniqAttrs, + }]); + } +} + +function checkCountLessThan(count: number | Promise, uniqAttrs: string[], than: number = 0, id?: string) { + if (count instanceof Promise) { + return count.then( + (count2) => { + if (count2 > than) { + throw new OakUniqueViolationException([{ + id, + attrs: uniqAttrs, + }]); + } + } + ) + } + if (count > than) { + throw new OakUniqueViolationException([{ + id, + attrs: uniqAttrs, + }]); + } +} + +function checkUnique | AsyncContext>( + entity: keyof ED, + row: Record, + context: Cxt, + uniqAttrs: string[], + extraFilter?: ED[keyof ED]['Selection']['filter'] +) { + const filter = pick(row, uniqAttrs); + for (const a in filter) { + if (filter[a] === null || filter[a] === undefined) { + delete filter[a]; + } + } + if (Object.keys(filter).length < uniqAttrs.length) { + // 说明有null值,不需要检查约束 + return; + } + const filter2 = extraFilter ? addFilterSegment([filter, extraFilter]) : filter; + const count = context.count(entity, { filter: filter2 }, { dontCollect: true }); + return checkCountLessThan(count, uniqAttrs, 0, row.id) +} + export function analyzeActionDefDict | AsyncContext>(schema: StorageSchema, actionDefDict: ActionDictOfEntityDict) { const checkers: Array> = []; const triggers: Array> = []; + + // action状态转换矩阵相应的checker for (const entity in actionDefDict) { for (const attr in actionDefDict[entity]) { const def = actionDefDict[entity]![attr]; @@ -92,7 +159,7 @@ export function analyzeActionDefDict { + checker: (data) => { if (data instanceof Array) { data.forEach( ele => { @@ -117,6 +184,125 @@ export function analyzeActionDefDict ele.name as string); + checkers.push({ + entity, + action: 'create', + type: 'logical', + priority: 20, // 优先级要放在最高,所有前置的checker/trigger将数据完整之后再在这里检测 + checker: (operation, context) => { + const { data } = operation; + + if (data instanceof Array) { + checkUniqueBetweenRows(data, uniqAttrs); + const checkResult = data.map( + ele => checkUnique(entity, ele, context, uniqAttrs) + ); + if (checkResult[0] instanceof Promise) { + return Promise.all(checkResult).then( + () => undefined + ); + } + } + else { + return checkUnique(entity, data, context, uniqAttrs); + } + } + }, { + entity, + action: 'update', // 只检查update,其它状态转换的action应该不会涉及unique约束的属性 + type: 'logical', + priority: 20, // 优先级要放在最高,所有前置的checker/trigger将数据完整之后再在这里检测 + checker: (operation, context) => { + const { data, filter: operationFilter } = operation as ED[keyof ED]['Update']; + const attrs = Object.keys(data); + + const refAttrs = intersection(attrs, uniqAttrs); + if (refAttrs.length === 0) { + // 如果本次更新和unique约束的属性之间没有交集则直接返回 + return; + } + for (const attr of refAttrs) { + // 如果有更新为null值,不用再检查约束 + if (data[attr as string] === null || data[attr as string] === undefined) { + return; + } + } + if (refAttrs.length === uniqAttrs.length) { + // 如果更新了全部属性,直接检查 + const filter = pick(data, refAttrs); + + // 在这些行以外的行不和更新后的键值冲突 + const count = context.count(entity, { + filter: addFilterSegment([filter, { + $not: operationFilter, + }]), + }, { dontCollect: true }); + const checkCount = checkCountLessThan(count, uniqAttrs); + + // 更新的行只能有一行 + const rowCount = context.count(entity, { + filter: operationFilter, + }, { dontCollect: true }); + const checkRowCount = checkCountLessThan(rowCount, uniqAttrs, 1); + + // 如果更新的行数为零似乎也可以,但这应该不可能出现吧,by Xc 20230131 + if (checkRowCount instanceof Promise) { + return Promise.all([checkCount, checkRowCount]).then( + () => undefined + ); + } + } + // 否则需要结合本行现有的属性来进行检查 + const projection = { id: 1 }; + for (const attr of uniqAttrs) { + Object.assign(projection, { + [attr]: 1, + }); + } + + const checkWithRows = (rows2: ED[keyof ED]['Schema'][]) => { + const rows22 = rows2.map( + ele => Object.assign(ele, data) + ); + // 先检查这些行本身之间是否冲突 + checkUniqueBetweenRows(rows22, uniqAttrs); + const checkResults = rows22.map( + (row) => checkUnique(entity, row, context, uniqAttrs, { + $not: operationFilter + }) + ); + if (checkResults[0] instanceof Promise) { + return Promise.all(checkResults).then( + () => undefined + ); + } + }; + + const currentRows = context.select(entity, { + data: projection, + filter: operationFilter, + }, { dontCollect: true }); + if (currentRows instanceof Promise) { + return currentRows.then( + (row2) => checkWithRows(row2 as ED[keyof ED]['Schema'][]) + ); + } + return checkWithRows(currentRows as ED[keyof ED]['Schema'][]); + } + }); + } + } + } + } + return { triggers, checkers, diff --git a/src/triggers/modi.ts b/src/triggers/modi.ts index ec407be..3008d26 100644 --- a/src/triggers/modi.ts +++ b/src/triggers/modi.ts @@ -24,7 +24,7 @@ const triggers: Trigger>[] = [ for (const modi of modies) { const { targetEntity, id, action, data, filter} = modi; await context.operate(targetEntity as keyof EntityDict, { - id, + id: id!, action: action as EntityDict[keyof EntityDict]['Action'], data: data as EntityDict[keyof EntityDict]['Update']['data'], filter: filter as EntityDict[keyof EntityDict]['Update']['filter'], diff --git a/src/types/Entity.ts b/src/types/Entity.ts index a0eb88a..47e6712 100644 --- a/src/types/Entity.ts +++ b/src/types/Entity.ts @@ -1,3 +1,4 @@ +import { ReadOnlyAction } from '../actions/action'; import { PrimaryKey, Sequence } from './DataType'; type TriggerDataAttributeType = '$$triggerData$$'; @@ -55,7 +56,17 @@ export type Operation = { - id?: string; // 为了一致性,每个operation也应当保证唯一id + id: string; // 为了一致性,每个operation也应当保证唯一id + action: A; + data: D; + sorter?: S; + } & FilterPart; + +export type Selection = { + id?: string; // selection的id可传可不传,如果传意味着该select会记录在oper中 action: A; data: D; sorter?: S; @@ -81,8 +92,8 @@ export interface EntityDef { OpSchema: GeneralEntityShape; Action: string; ParticularAction?: string; - Selection: Omit, 'action'>; - Aggregation: Omit, 'action'>; + Selection: Omit, 'action'>; + Aggregation: DeduceAggregation; Operation: CUDOperation; Create: CreateOperation; CreateSingle: CreateSingleOperation; @@ -140,7 +151,7 @@ type Projection = { export type DeduceAggregation< P extends Projection, F extends Filter, - S extends Sorter> = Omit, F, S>, 'action'>; + S extends Sorter> = Omit, F, S>, 'action'>; type CreateOperationData = { id: string; diff --git a/src/types/Exception.ts b/src/types/Exception.ts index 8ef5b78..d40427a 100644 --- a/src/types/Exception.ts +++ b/src/types/Exception.ts @@ -26,6 +26,20 @@ export class OakDataException extends OakException { // 表示由数据层发现的异常 } +export class OakUniqueViolationException extends OakException { + rows: Array<{ + id?: string; + attrs: string[]; + }>; + constructor(rows: Array<{ + id?: string; + attrs: string[]; + }>, message?: string) { + super(message || '您更新的数据违反了唯一性约束'); + this.rows = rows; + } +} + export class OakImportDataParseException extends OakException { line: number; header?: string; @@ -232,6 +246,12 @@ export function makeException(data: { case 'OakDeadlock': { return new OakDeadlock(data.message); } + case 'OakDataException': { + return new OakDataException(data.message); + } + case 'OakUniqueViolationException': { + return new OakUniqueViolationException(data.rows, data.message); + } case 'OakImportDataParseException': { return new OakImportDataParseException(data.message!, data.line, data.header); } diff --git a/src/utils/uuid.ts b/src/utils/uuid.ts index 5419499..3554d4c 100644 --- a/src/utils/uuid.ts +++ b/src/utils/uuid.ts @@ -127,7 +127,7 @@ export function sequentialUuid({ random }: { random: Uint8Array }) { } export function shrinkUuidTo32Bytes(uuid: string) { - return uuid.replaceAll('-', ''); + return uuid.replace(/\-/g, ''); } export function expandUuidTo36Bytes(uuidShrinked: string) {