@ruiapp/rapid-core 0.10.2 → 0.10.4

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.
@@ -0,0 +1,9 @@
1
+ import { RouteContext } from "../core/routeContext";
2
+ /**
3
+ * 在事务中执行`func`
4
+ * @param routeContext
5
+ * @param func
6
+ * @returns
7
+ */
8
+ export declare function executeInDbTransaction<TResult>(routeContext: RouteContext, func: () => Promise<TResult>): Promise<TResult>;
9
+ export declare function executeInRouteContext<TResult>(routeContext: RouteContext, inDbTransaction: boolean | undefined | null, func: () => Promise<TResult>): Promise<TResult>;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export * from "./helpers/licenseHelper";
23
23
  export * as entityHelper from "./helpers/entityHelper";
24
24
  export * as licenseHelper from "./helpers/licenseHelper";
25
25
  export * as metaHelper from "./helpers/metaHelper";
26
+ export * from "./helpers/dbTransactionHelper";
26
27
  export * from "./deno-std/http/cookie";
27
28
  export { mapDbRowToEntity } from "./dataAccess/entityMapper";
28
29
  export * as bootstrapApplicationConfig from "./bootstrapApplicationConfig";
package/dist/index.js CHANGED
@@ -1582,6 +1582,13 @@ var bootstrapApplicationConfig = {
1582
1582
  type: "text",
1583
1583
  required: false,
1584
1584
  },
1585
+ {
1586
+ name: "target type column name",
1587
+ code: "targetTypeColumnName",
1588
+ columnName: "target_type_column_name",
1589
+ type: "text",
1590
+ required: false,
1591
+ },
1585
1592
  {
1586
1593
  name: "target id column name",
1587
1594
  code: "targetIdColumnName",
@@ -4996,7 +5003,7 @@ class RapidRequest {
4996
5003
  else if (contentType.startsWith("multipart/form-data")) {
4997
5004
  this.#body = {
4998
5005
  type: "form-data",
4999
- value: await parseFormDataBody(req),
5006
+ value: await parseFormDataBody(req, { all: true }),
5000
5007
  };
5001
5008
  }
5002
5009
  this.#bodyParsed = true;
@@ -5380,6 +5387,43 @@ var licenseHelper = /*#__PURE__*/Object.freeze({
5380
5387
  tryValidateLicense: tryValidateLicense
5381
5388
  });
5382
5389
 
5390
+ /**
5391
+ * 在事务中执行`func`
5392
+ * @param routeContext
5393
+ * @param func
5394
+ * @returns
5395
+ */
5396
+ async function executeInDbTransaction(routeContext, func) {
5397
+ if (!routeContext) {
5398
+ throw new Error(`Patameter "routeContext" is required if you want run in transaction.`);
5399
+ }
5400
+ let transactionDbClient;
5401
+ try {
5402
+ transactionDbClient = await routeContext.initDbTransactionClient();
5403
+ await routeContext.beginDbTransaction();
5404
+ const result = await func();
5405
+ await routeContext.commitDbTransaction();
5406
+ return result;
5407
+ }
5408
+ catch (ex) {
5409
+ await routeContext.rollbackDbTransaction();
5410
+ throw ex;
5411
+ }
5412
+ finally {
5413
+ if (transactionDbClient) {
5414
+ transactionDbClient.release();
5415
+ }
5416
+ }
5417
+ }
5418
+ async function executeInRouteContext(routeContext, inDbTransaction, func) {
5419
+ if (inDbTransaction) {
5420
+ return await executeInDbTransaction(routeContext, func);
5421
+ }
5422
+ else {
5423
+ return await func();
5424
+ }
5425
+ }
5426
+
5383
5427
  const values = new Map();
5384
5428
  async function set(key, value, options) {
5385
5429
  let expireAt = -1;
@@ -8279,7 +8323,9 @@ class SequencePlugin {
8279
8323
  const code$5 = "runServerOperation";
8280
8324
  async function handler$5(plugin, ctx, options) {
8281
8325
  const { operation } = options;
8282
- await operation(ctx);
8326
+ await executeInRouteContext(ctx.routerContext, options.executeInDbTransaction, async () => {
8327
+ await operation(ctx);
8328
+ });
8283
8329
  }
8284
8330
 
8285
8331
  var runServerOperation = /*#__PURE__*/Object.freeze({
@@ -8339,6 +8385,7 @@ class ServerOperationPlugin {
8339
8385
  config: {
8340
8386
  operation: operation.handler,
8341
8387
  permissionCheck: operation.permissionCheck,
8388
+ executeInDbTransaction: operation.executeInDbTransaction,
8342
8389
  },
8343
8390
  },
8344
8391
  ],
@@ -9360,13 +9407,15 @@ class CronJobService {
9360
9407
  let lastErrorStack;
9361
9408
  try {
9362
9409
  validateLicense(server);
9363
- if (job.actionHandlerCode) {
9364
- const actionHandler = server.getActionHandlerByCode(job.code);
9365
- await actionHandler(handlerContext, job.handleOptions);
9366
- }
9367
- else {
9368
- await job.handler(handlerContext, job.handleOptions);
9369
- }
9410
+ await executeInRouteContext(handlerContext.routerContext, job.executeInDbTransaction, async () => {
9411
+ if (job.actionHandlerCode) {
9412
+ const actionHandler = server.getActionHandlerByCode(job.code);
9413
+ await actionHandler(handlerContext, job.handleOptions);
9414
+ }
9415
+ else {
9416
+ await job.handler(handlerContext, job.handleOptions);
9417
+ }
9418
+ });
9370
9419
  result = "success";
9371
9420
  logger.info(`Completed cron job '${jobCode}'...`);
9372
9421
  }
@@ -9947,6 +9996,8 @@ exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
9947
9996
  exports.ensureDirectoryExists = ensureDirectoryExists;
9948
9997
  exports.entityHelper = entityHelper;
9949
9998
  exports.enumFileBaseNamesInDirectory = enumFileBaseNamesInDirectory;
9999
+ exports.executeInDbTransaction = executeInDbTransaction;
10000
+ exports.executeInRouteContext = executeInRouteContext;
9950
10001
  exports.formatDateTimeWithTimezone = formatDateTimeWithTimezone;
9951
10002
  exports.generateJwtSecretKey = generateJwtSecretKey;
9952
10003
  exports.generatePasswordHash = generatePasswordHash;
@@ -6,6 +6,10 @@ export interface ServerOperation {
6
6
  description?: string;
7
7
  method: RpdHttpMethod;
8
8
  permissionCheck?: PermissionCheckPolicy;
9
+ /**
10
+ * 是否在事务中执行
11
+ */
12
+ executeInDbTransaction?: boolean;
9
13
  handler: (ctx: ActionHandlerContext) => Promise<void>;
10
14
  }
11
15
  export interface ServerOperationPluginInitOptions {
@@ -3,4 +3,5 @@ import { RapidPlugin } from "../../../core/server";
3
3
  export declare const code = "runServerOperation";
4
4
  export declare function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: {
5
5
  operation: (ctx: ActionHandlerContext) => Promise<void>;
6
+ executeInDbTransaction?: boolean;
6
7
  }): Promise<void>;
@@ -20,6 +20,10 @@ export interface CronJobConfiguration {
20
20
  * 任务设置
21
21
  */
22
22
  jobOptions?: CronJobOptions;
23
+ /**
24
+ * 是否在事务中执行
25
+ */
26
+ executeInDbTransaction?: boolean;
23
27
  /**
24
28
  * 任务处理程序编号。当指定处理程序编号时,忽略 handler 配置。
25
29
  */
package/dist/types.d.ts CHANGED
@@ -315,6 +315,10 @@ export interface RpdDataModelProperty {
315
315
  * 关联实体的singular code,不管 relation 为 one 或者 many 都需要设置。
316
316
  */
317
317
  targetSingularCode?: string;
318
+ /**
319
+ * 保存关联实体类型的字段名。当设置 targetTypeColumnName 时,表示关联实体类型不固定,可以关联不同类型的实体。
320
+ */
321
+ targetTypeColumnName?: string;
318
322
  /**
319
323
  * 当 relation 为 one 时,设置当前模型表中表示关联实体 id 的列名。
320
324
  * 当 relation 为 many,并且使用关联关系表保存关联信息时,设置关联关系表中表示关联实体 id 的列名。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -350,6 +350,13 @@ export default {
350
350
  type: "text",
351
351
  required: false,
352
352
  },
353
+ {
354
+ name: "target type column name",
355
+ code: "targetTypeColumnName",
356
+ columnName: "target_type_column_name",
357
+ type: "text",
358
+ required: false,
359
+ },
353
360
  {
354
361
  name: "target id column name",
355
362
  code: "targetIdColumnName",
@@ -1,5 +1,3 @@
1
- import type { RapidRequest } from "../request";
2
-
3
1
  export type BodyData = Record<string, string | File | (string | File)[]>;
4
2
  export type ParseBodyOptions = {
5
3
  /**
@@ -66,7 +66,7 @@ export class RapidRequest {
66
66
  } else if (contentType.startsWith("multipart/form-data")) {
67
67
  this.#body = {
68
68
  type: "form-data",
69
- value: await parseFormDataBody(req),
69
+ value: await parseFormDataBody(req, { all: true }),
70
70
  };
71
71
  }
72
72
  this.#bodyParsed = true;
@@ -0,0 +1,42 @@
1
+ import { RouteContext } from "~/core/routeContext";
2
+ import { IDatabaseClient } from "~/types";
3
+
4
+ /**
5
+ * 在事务中执行`func`
6
+ * @param routeContext
7
+ * @param func
8
+ * @returns
9
+ */
10
+ export async function executeInDbTransaction<TResult>(routeContext: RouteContext, func: () => Promise<TResult>): Promise<TResult> {
11
+ if (!routeContext) {
12
+ throw new Error(`Patameter "routeContext" is required if you want run in transaction.`);
13
+ }
14
+ let transactionDbClient: IDatabaseClient | undefined;
15
+
16
+ try {
17
+ transactionDbClient = await routeContext.initDbTransactionClient();
18
+ await routeContext.beginDbTransaction();
19
+ const result = await func();
20
+ await routeContext.commitDbTransaction();
21
+ return result;
22
+ } catch (ex) {
23
+ await routeContext.rollbackDbTransaction();
24
+ throw ex;
25
+ } finally {
26
+ if (transactionDbClient) {
27
+ transactionDbClient.release();
28
+ }
29
+ }
30
+ }
31
+
32
+ export async function executeInRouteContext<TResult>(
33
+ routeContext: RouteContext,
34
+ inDbTransaction: boolean | undefined | null,
35
+ func: () => Promise<TResult>,
36
+ ): Promise<TResult> {
37
+ if (inDbTransaction) {
38
+ return await executeInDbTransaction(routeContext, func);
39
+ } else {
40
+ return await func();
41
+ }
42
+ }
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ export * from "./helpers/licenseHelper";
31
31
  export * as entityHelper from "./helpers/entityHelper";
32
32
  export * as licenseHelper from "./helpers/licenseHelper";
33
33
  export * as metaHelper from "./helpers/metaHelper";
34
+ export * from "./helpers/dbTransactionHelper";
34
35
 
35
36
  export * from "./deno-std/http/cookie";
36
37
 
@@ -7,6 +7,7 @@ import { JobRunningResult, NamedCronJobInstance, SysCronJob, UpdateJobConfigOpti
7
7
  import { CronJobConfiguration } from "~/types/cron-job-types";
8
8
  import { ActionHandlerContext } from "~/core/actionHandler";
9
9
  import { formatDateTimeWithTimezone, getNowStringWithTimezone } from "~/utilities/timeUtility";
10
+ import { executeInRouteContext } from "~/helpers/dbTransactionHelper";
10
11
 
11
12
  export default class CronJobService {
12
13
  #server: IRpdServer;
@@ -144,12 +145,15 @@ export default class CronJobService {
144
145
  try {
145
146
  validateLicense(server);
146
147
 
147
- if (job.actionHandlerCode) {
148
- const actionHandler = server.getActionHandlerByCode(job.code);
149
- await actionHandler(handlerContext, job.handleOptions);
150
- } else {
151
- await job.handler(handlerContext, job.handleOptions);
152
- }
148
+ await executeInRouteContext(handlerContext.routerContext, job.executeInDbTransaction, async () => {
149
+ if (job.actionHandlerCode) {
150
+ const actionHandler = server.getActionHandlerByCode(job.code);
151
+ await actionHandler(handlerContext, job.handleOptions);
152
+ } else {
153
+ await job.handler(handlerContext, job.handleOptions);
154
+ }
155
+ });
156
+
153
157
  result = "success";
154
158
  logger.info(`Completed cron job '${jobCode}'...`);
155
159
  } catch (ex: any) {
@@ -75,6 +75,7 @@ class ServerOperationPlugin implements RapidPlugin {
75
75
  config: {
76
76
  operation: operation.handler,
77
77
  permissionCheck: operation.permissionCheck,
78
+ executeInDbTransaction: operation.executeInDbTransaction,
78
79
  },
79
80
  },
80
81
  ],
@@ -7,6 +7,11 @@ export interface ServerOperation {
7
7
  description?: string;
8
8
  method: RpdHttpMethod;
9
9
  permissionCheck?: PermissionCheckPolicy;
10
+
11
+ /**
12
+ * 是否在事务中执行
13
+ */
14
+ executeInDbTransaction?: boolean;
10
15
  handler: (ctx: ActionHandlerContext) => Promise<void>;
11
16
  }
12
17
 
@@ -1,5 +1,6 @@
1
1
  import { ActionHandlerContext } from "~/core/actionHandler";
2
2
  import { RapidPlugin } from "~/core/server";
3
+ import { executeInDbTransaction, executeInRouteContext } from "~/helpers/dbTransactionHelper";
3
4
 
4
5
  export const code = "runServerOperation";
5
6
 
@@ -8,8 +9,12 @@ export async function handler(
8
9
  ctx: ActionHandlerContext,
9
10
  options: {
10
11
  operation: (ctx: ActionHandlerContext) => Promise<void>;
12
+ executeInDbTransaction?: boolean;
11
13
  },
12
14
  ) {
13
15
  const { operation } = options;
14
- await operation(ctx);
16
+
17
+ await executeInRouteContext(ctx.routerContext, options.executeInDbTransaction, async () => {
18
+ await operation(ctx);
19
+ });
15
20
  }
@@ -26,6 +26,11 @@ export interface CronJobConfiguration {
26
26
  */
27
27
  jobOptions?: CronJobOptions;
28
28
 
29
+ /**
30
+ * 是否在事务中执行
31
+ */
32
+ executeInDbTransaction?: boolean;
33
+
29
34
  /**
30
35
  * 任务处理程序编号。当指定处理程序编号时,忽略 handler 配置。
31
36
  */
package/src/types.ts CHANGED
@@ -359,6 +359,10 @@ export interface RpdDataModelProperty {
359
359
  * 关联实体的singular code,不管 relation 为 one 或者 many 都需要设置。
360
360
  */
361
361
  targetSingularCode?: string;
362
+ /**
363
+ * 保存关联实体类型的字段名。当设置 targetTypeColumnName 时,表示关联实体类型不固定,可以关联不同类型的实体。
364
+ */
365
+ targetTypeColumnName?: string;
362
366
  /**
363
367
  * 当 relation 为 one 时,设置当前模型表中表示关联实体 id 的列名。
364
368
  * 当 relation 为 many,并且使用关联关系表保存关联信息时,设置关联关系表中表示关联实体 id 的列名。