@ruiapp/rapid-core 0.1.29 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/core/pluginManager.d.ts +3 -1
  2. package/dist/core/server.d.ts +4 -1
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +411 -135
  5. package/dist/plugins/sequence/SequencePluginTypes.d.ts +50 -0
  6. package/dist/plugins/stateMachine/StateMachinePlugin.d.ts +36 -0
  7. package/dist/plugins/stateMachine/StateMachinePluginTypes.d.ts +24 -0
  8. package/dist/plugins/stateMachine/StateMachineService.d.ts +3 -0
  9. package/dist/plugins/stateMachine/actionHandlers/index.d.ts +3 -0
  10. package/dist/plugins/stateMachine/actionHandlers/sendStateMachineEvent.d.ts +5 -0
  11. package/dist/plugins/stateMachine/models/StateMachine.d.ts +3 -0
  12. package/dist/plugins/stateMachine/models/index.d.ts +2 -0
  13. package/dist/plugins/stateMachine/routes/index.d.ts +12 -0
  14. package/dist/plugins/stateMachine/routes/sendStateMachineEvent.d.ts +12 -0
  15. package/dist/server.d.ts +2 -1
  16. package/dist/types.d.ts +2 -43
  17. package/package.json +3 -2
  18. package/src/core/pluginManager.ts +14 -1
  19. package/src/core/server.ts +4 -1
  20. package/src/dataAccess/entityManager.ts +6 -3
  21. package/src/index.ts +14 -1
  22. package/src/plugins/dataManage/actionHandlers/updateCollectionEntityById.ts +7 -1
  23. package/src/plugins/sequence/SequencePlugin.ts +11 -12
  24. package/src/plugins/sequence/SequencePluginTypes.ts +70 -0
  25. package/src/plugins/sequence/SequenceService.ts +1 -1
  26. package/src/plugins/sequence/actionHandlers/generateSn.ts +0 -1
  27. package/src/plugins/stateMachine/StateMachinePlugin.ts +175 -0
  28. package/src/plugins/stateMachine/StateMachinePluginTypes.ts +30 -0
  29. package/src/plugins/stateMachine/StateMachineService.ts +19 -0
  30. package/src/plugins/stateMachine/actionHandlers/index.ts +6 -0
  31. package/src/plugins/stateMachine/actionHandlers/sendStateMachineEvent.ts +55 -0
  32. package/src/plugins/stateMachine/models/StateMachine.ts +42 -0
  33. package/src/plugins/stateMachine/models/index.ts +5 -0
  34. package/src/plugins/stateMachine/routes/index.ts +5 -0
  35. package/src/plugins/stateMachine/routes/sendStateMachineEvent.ts +15 -0
  36. package/src/server.ts +5 -0
  37. package/src/types.ts +2 -62
  38. package/dist/plugins/sequence/sequence-types.d.ts +0 -8
  39. package/src/plugins/sequence/sequence-types.ts +0 -10
@@ -0,0 +1,175 @@
1
+ /**
2
+ * State machine plugin
3
+ */
4
+
5
+ import {
6
+ CreateEntityOptions,
7
+ RpdApplicationConfig,
8
+ RpdDataModel,
9
+ RpdDataModelProperty,
10
+ UpdateEntityByIdOptions,
11
+ } from "~/types";
12
+ import { IRpdServer, RapidPlugin, RpdConfigurationItemOptions, RpdServerPluginConfigurableTargetOptions, RpdServerPluginExtendingAbilities } from "~/core/server";
13
+
14
+ import pluginActionHandlers from "./actionHandlers";
15
+ import pluginModels from "./models";
16
+ import pluginRoutes from "./routes";
17
+ import { filter, find, first, get, isEqual } from "lodash";
18
+ import { PropertyStateMachineConfig } from "./StateMachinePluginTypes";
19
+ import { isNullOrUndefined } from "~/utilities/typeUtility";
20
+ import { getStateMachineNextSnapshot } from "./StateMachineService";
21
+
22
+
23
+ class StateMachinePlugin implements RapidPlugin {
24
+ get code(): string {
25
+ return "stateMachinePlugin";
26
+ }
27
+
28
+ get description(): string {
29
+ return null;
30
+ }
31
+
32
+ get extendingAbilities(): RpdServerPluginExtendingAbilities[] {
33
+ return [];
34
+ }
35
+
36
+ get configurableTargets(): RpdServerPluginConfigurableTargetOptions[] {
37
+ return [];
38
+ }
39
+
40
+ get configurations(): RpdConfigurationItemOptions[] {
41
+ return [];
42
+ }
43
+
44
+ async registerActionHandlers(server: IRpdServer): Promise<any> {
45
+ for (const actionHandler of pluginActionHandlers) {
46
+ server.registerActionHandler(this, actionHandler);
47
+ }
48
+ }
49
+
50
+ async configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
51
+ server.appendApplicationConfig({ models: pluginModels });
52
+ }
53
+
54
+ async configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
55
+ server.appendApplicationConfig({ routes: pluginRoutes });
56
+ }
57
+
58
+ async onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig) {
59
+ const models = server.getApplicationConfig().models;
60
+ for (const model of models) {
61
+ for (const property of model.properties) {
62
+ const propertyStateMachineConfig: PropertyStateMachineConfig = property.config?.stateMachine;
63
+ if (propertyStateMachineConfig) {
64
+ const stateMachineCode = getStateMachineCode(model, property);
65
+ const stateMachineConfig = propertyStateMachineConfig.config;
66
+
67
+ const stateMachineDataAccessor = server.getDataAccessor({
68
+ singularCode: "state_machine",
69
+ });
70
+ const stateMachine = await stateMachineDataAccessor.findOne({
71
+ filters: [
72
+ {
73
+ operator: "eq",
74
+ field: "code",
75
+ value: stateMachineCode,
76
+ },
77
+ ],
78
+ });
79
+
80
+ if (stateMachine) {
81
+ if (!isEqual(stateMachine.config, stateMachineConfig)) {
82
+ await stateMachineDataAccessor.updateById(stateMachine.id, {
83
+ config: stateMachineConfig,
84
+ });
85
+ }
86
+ } else {
87
+ await stateMachineDataAccessor.create({
88
+ code: stateMachineCode,
89
+ config: stateMachineConfig,
90
+ });
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * 创建实体前的处理。
99
+ * 当属性启用了状态机管理,如创建实体时没有指定该属性的状态值,则应将该属性设置为 stateMachine.config.initial 。
100
+ * @param server
101
+ * @param model
102
+ * @param options
103
+ */
104
+ async beforeCreateEntity(server: IRpdServer, model: RpdDataModel, options: CreateEntityOptions) {
105
+ for (const property of model.properties) {
106
+ const isStateMachineEnabled = get(property.config, "stateMachine.enabled", false);
107
+ if (isStateMachineEnabled && isNullOrUndefined(options.entity[property.code])
108
+ ) {
109
+ const initialState = get(property.config, "stateMachine.config.initial", null);
110
+ if (initialState) {
111
+ options.entity[property.code] = initialState;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * 更新实体前的处理。
119
+ * 1. 对所有启用了状态机管理,且 transferControl 为 true 的属性,应禁止直接更新这些属性
120
+ * 2. 当更新实体时指定了operation,则查找启用了状态机管理的属性。
121
+ * 如果一个模型中存在多个属性启用了状态机管理,则以 options.stateProperty 中指定的为准。
122
+ * 对于该状态属性应用 options.operation,使其转换到下一状态。
123
+ * @param server
124
+ * @param model
125
+ * @param options
126
+ */
127
+ async beforeUpdateEntity(server: IRpdServer, model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any) {
128
+ const entity = options.entityToSave;
129
+
130
+ for (const property of model.properties) {
131
+ const isStateMachineEnabled = get(property.config, "stateMachine.enabled", false);
132
+ const isTransferControlEnabled = get(property.config, "stateMachine.transferControl", false);
133
+ if (isStateMachineEnabled && isTransferControlEnabled && !isNullOrUndefined(entity[property.code])
134
+ ) {
135
+ throw new Error(`You're not allowed to change '${property.code}' property directly when transfer control is enabled, do an operation instead.`);
136
+ }
137
+ }
138
+
139
+ if (!options.operation) {
140
+ return;
141
+ }
142
+
143
+ const stateMachineEnabledProperties = filter(model.properties, (property) => get(property.config, "stateMachine.enabled", false)) as RpdDataModelProperty[];
144
+ let stateMachineEnabledProperty = first(stateMachineEnabledProperties);
145
+ if (options.stateProperty) {
146
+ stateMachineEnabledProperty = find(stateMachineEnabledProperties, (property) => property.code === options.stateProperty);
147
+ }
148
+
149
+ if (!stateMachineEnabledProperty) {
150
+ throw new Error(`State machine property not found.`);
151
+ }
152
+
153
+ const machineConfig = get(stateMachineEnabledProperty.config, "stateMachine.config", null);
154
+ if (!machineConfig) {
155
+ throw new Error(`State machine of property '${stateMachineEnabledProperty.code}' not configured.`);
156
+ }
157
+ machineConfig.id = getStateMachineCode(model, stateMachineEnabledProperty);
158
+
159
+ const nextSnapshot = await getStateMachineNextSnapshot(server, {
160
+ machineConfig,
161
+ context: {},
162
+ currentState: currentEntity[stateMachineEnabledProperty.code],
163
+ event: options.operation,
164
+ });
165
+
166
+ entity[stateMachineEnabledProperty.code] = nextSnapshot.value;
167
+ }
168
+
169
+ }
170
+
171
+ function getStateMachineCode(model: RpdDataModel, property: RpdDataModelProperty) {
172
+ return `propertyStateMachine.${model.namespace}.${model.singularCode}.${property.code}`;
173
+ }
174
+
175
+ export default StateMachinePlugin;
@@ -0,0 +1,30 @@
1
+ import { MachineConfig } from "xstate";
2
+
3
+
4
+ export type PropertyStateMachineConfig = {
5
+ enabled: boolean;
6
+ config: MachineConfig<any, any>;
7
+ transferControl?: boolean;
8
+ }
9
+
10
+ export type SendStateMachineEventOptions = {
11
+ code: string;
12
+ }
13
+
14
+ export type SendStateMachineEventInput = {
15
+ code?: string;
16
+ context: any;
17
+ currentState: string;
18
+ event: StateMachineEvent;
19
+ }
20
+
21
+ export type StateMachineEvent = {
22
+ type: string;
23
+ }
24
+
25
+ export type GetStateMachineNextSnapshotOptions = {
26
+ machineConfig: MachineConfig<any, any>;
27
+ context: any;
28
+ currentState: string;
29
+ event: StateMachineEvent;
30
+ }
@@ -0,0 +1,19 @@
1
+ import { IRpdServer } from "~/core/server";
2
+ import { GetStateMachineNextSnapshotOptions } from "./StateMachinePluginTypes";
3
+ import { createMachine, getInitialSnapshot, getNextSnapshot } from "xstate";
4
+
5
+ export async function getStateMachineNextSnapshot(server: IRpdServer, options: GetStateMachineNextSnapshotOptions) {
6
+ debugger
7
+ const { machineConfig, currentState, event } = options;
8
+ machineConfig.initial = currentState;
9
+
10
+ const machine = createMachine(machineConfig);
11
+ const snapshot = getInitialSnapshot(machine);
12
+
13
+ if (!snapshot.can(event)) {
14
+ throw new Error(`'${event.type}' action is not allowed at '${currentState}' state.`);
15
+ }
16
+
17
+ const nextSnapshot = getNextSnapshot(machine, snapshot, event);
18
+ return nextSnapshot;
19
+ }
@@ -0,0 +1,6 @@
1
+ import { IPluginActionHandler } from "~/core/actionHandler";
2
+ import * as sendStateMachineEvent from "./sendStateMachineEvent";
3
+
4
+ export default [
5
+ sendStateMachineEvent,
6
+ ] satisfies IPluginActionHandler[];
@@ -0,0 +1,55 @@
1
+ import { ActionHandlerContext } from "~/core/actionHandler";
2
+ import { RapidPlugin } from "~/core/server";
3
+ import { SendStateMachineEventInput, SendStateMachineEventOptions } from "../StateMachinePluginTypes";
4
+ import { getStateMachineNextSnapshot } from "../StateMachineService";
5
+
6
+ export const code = "sendStateMachineEvent";
7
+
8
+ export async function handler(
9
+ plugin: RapidPlugin,
10
+ ctx: ActionHandlerContext,
11
+ options: SendStateMachineEventOptions,
12
+ ) {
13
+ const { server, routerContext } = ctx;
14
+ const { response } = routerContext;
15
+
16
+ const input: SendStateMachineEventInput = ctx.input;
17
+ if (options?.code) {
18
+ input.code = options.code;
19
+ }
20
+
21
+ if (!input.code) {
22
+ throw new Error(`State machine code is required when sending event.`);
23
+ }
24
+
25
+ const stateMachineDataAccessor = server.getDataAccessor({
26
+ singularCode: "state_machine",
27
+ });
28
+
29
+ const stateMachine = await stateMachineDataAccessor.findOne({
30
+ filters: [
31
+ {
32
+ operator: "eq",
33
+ field: "code",
34
+ value: input.code,
35
+ }
36
+ ]
37
+ });
38
+
39
+ if (!stateMachine) {
40
+ throw new Error(`State machine with code '${input.code}' was not found.`);
41
+ }
42
+
43
+ stateMachine.config.id = input.code;
44
+
45
+ const snapshot = await getStateMachineNextSnapshot(server, {
46
+ machineConfig: stateMachine.config,
47
+ context: input.context,
48
+ currentState: input.currentState,
49
+ event: input.event,
50
+ });
51
+
52
+ response.json({
53
+ state: snapshot.value,
54
+ });
55
+ }
@@ -0,0 +1,42 @@
1
+ import { RpdDataModel } from "~/types";
2
+
3
+ export default {
4
+ maintainedBy: "stateMachinePlugin",
5
+ namespace: "svc",
6
+ name: "state_machine",
7
+ singularCode: "state_machine",
8
+ pluralCode: "state_machines",
9
+ schema: "public",
10
+ tableName: "state_machines",
11
+ properties: [
12
+ {
13
+ name: "id",
14
+ code: "id",
15
+ columnName: "id",
16
+ type: "integer",
17
+ required: true,
18
+ autoIncrement: true,
19
+ },
20
+ {
21
+ name: "code",
22
+ code: "code",
23
+ columnName: "code",
24
+ type: "text",
25
+ required: true,
26
+ },
27
+ {
28
+ name: "description",
29
+ code: "description",
30
+ columnName: "description",
31
+ type: "text",
32
+ required: false,
33
+ },
34
+ {
35
+ name: "config",
36
+ code: "config",
37
+ columnName: "config",
38
+ type: "json",
39
+ required: false,
40
+ },
41
+ ],
42
+ } as RpdDataModel;
@@ -0,0 +1,5 @@
1
+ import StateMachine from "./StateMachine";
2
+
3
+ export default [
4
+ StateMachine,
5
+ ]
@@ -0,0 +1,5 @@
1
+ import sendStateMachineEvent from "./sendStateMachineEvent";
2
+
3
+ export default [
4
+ sendStateMachineEvent,
5
+ ]
@@ -0,0 +1,15 @@
1
+ import { RpdRoute } from "~/types";
2
+
3
+ export default {
4
+ namespace: "svc",
5
+ name: "svc.sendStateMachineEvent",
6
+ code: "svc.sendStateMachineEvent",
7
+ type: "RESTful",
8
+ method: "POST",
9
+ endpoint: "/svc/sendStateMachineEvent",
10
+ actions: [
11
+ {
12
+ code: "sendStateMachineEvent",
13
+ },
14
+ ],
15
+ } satisfies RpdRoute;
package/src/server.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  RapidServerConfig,
13
13
  RpdDataModelProperty,
14
14
  CreateEntityOptions,
15
+ UpdateEntityByIdOptions,
15
16
  } from "./types";
16
17
 
17
18
  import QueryBuilder from "./queryBuilder/queryBuilder";
@@ -335,4 +336,8 @@ export class RapidServer implements IRpdServer {
335
336
  async beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions) {
336
337
  await this.#pluginManager.beforeCreateEntity(model, options);
337
338
  }
339
+
340
+ async beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any) {
341
+ await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
342
+ }
338
343
  }
package/src/types.ts CHANGED
@@ -451,6 +451,8 @@ export interface UpdateEntityOptions {
451
451
  export interface UpdateEntityByIdOptions {
452
452
  id: any;
453
453
  entityToSave: any;
454
+ operation?: any;
455
+ stateProperty?: string;
454
456
  }
455
457
 
456
458
  export interface DeleteEntityOptions {
@@ -468,65 +470,3 @@ export interface RemoveEntityRelationsOptions {
468
470
  property: string;
469
471
  relations: {id?: number, [k: string]: any}[];
470
472
  }
471
-
472
-
473
- export type SequenceSegmentConfig =
474
- | SequenceLiteralSegmentConfig
475
- | SequenceYearSegmentConfig
476
- | SequenceMonthSegmentConfig
477
- | SequenceDayOfMonthSegmentConfig
478
- | SequenceDayOfWeekSegmentConfig
479
- | SequenceDayOfYearSegmentConfig
480
- | SequenceParameterSegmentConfig
481
- | SequenceAutoIncrementSegmentConfig
482
- ;
483
-
484
- export type SequenceLiteralSegmentConfig = {
485
- type: "literal",
486
- content: string;
487
- }
488
-
489
- export type SequenceYearSegmentConfig = {
490
- type: "year",
491
- padding?: string;
492
- length?: number;
493
- }
494
-
495
- export type SequenceMonthSegmentConfig = {
496
- type: "month",
497
- padding?: string;
498
- length?: number;
499
- }
500
-
501
- export type SequenceDayOfMonthSegmentConfig = {
502
- type: "dayOfMonth",
503
- padding?: string;
504
- length?: number;
505
- }
506
-
507
- export type SequenceDayOfWeekSegmentConfig = {
508
- type: "dayOfWeek",
509
- padding?: string;
510
- length?: number;
511
- }
512
-
513
- export type SequenceDayOfYearSegmentConfig = {
514
- type: "dayOfYear",
515
- padding?: string;
516
- length?: number;
517
- }
518
-
519
- export type SequenceParameterSegmentConfig = {
520
- type: "parameter",
521
- parameterName: string;
522
- padding?: string;
523
- length?: number;
524
- }
525
-
526
- export type SequenceAutoIncrementSegmentConfig = {
527
- type: "autoIncrement",
528
- scope: string;
529
- period: "forever" | "day" | "month" | "year";
530
- padding?: string;
531
- length?: number;
532
- }
@@ -1,8 +0,0 @@
1
- import { SequenceSegmentConfig } from "../../types";
2
- export type PropertySequenceConfig = {
3
- autoGenerate: boolean;
4
- ruleConfig: SequenceRuleConfig;
5
- };
6
- export type SequenceRuleConfig = {
7
- segments: SequenceSegmentConfig[];
8
- };
@@ -1,10 +0,0 @@
1
- import { SequenceSegmentConfig } from "~/types";
2
-
3
- export type PropertySequenceConfig = {
4
- autoGenerate: boolean;
5
- ruleConfig: SequenceRuleConfig;
6
- }
7
-
8
- export type SequenceRuleConfig = {
9
- segments: SequenceSegmentConfig[];
10
- }