@ruiapp/rapid-core 0.10.4 → 0.10.6

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,2 @@
1
+ export declare function interpreteExpression(expressionString: string, rootVars: Record<string, any>): any;
2
+ export declare function interpreteConfigExpressions(config: Record<string, any>, vars: Record<string, any>): void;
@@ -1,15 +1,15 @@
1
- import { RpdApplicationConfig } from "../types";
2
1
  import { IRpdServer, RapidPlugin } from "./server";
3
2
  import { Next, RouteContext } from "./routeContext";
4
3
  import { Logger } from "../facilities/log/LogFacility";
5
4
  export interface ActionHandlerContext {
6
5
  logger: Logger;
7
6
  routerContext: RouteContext;
8
- next: Next;
7
+ next?: Next;
9
8
  server: IRpdServer;
10
- applicationConfig: RpdApplicationConfig;
9
+ vars?: Record<string, any>;
11
10
  input?: any;
12
11
  output?: any;
12
+ results: any[];
13
13
  status?: Response["status"];
14
14
  }
15
15
  export type ActionHandler = (ctx: ActionHandlerContext, options: any) => void | Promise<void>;
@@ -31,6 +31,7 @@ export interface IRpdServer {
31
31
  registerEntityWatcher(entityWatcher: EntityWatcherType): any;
32
32
  emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>): void;
33
33
  handleRequest(request: RapidRequest, next: Next): Promise<Response>;
34
+ runActionHandlers(handlerContext: ActionHandlerContext, actions: RpdRouteActionConfig[]): Promise<void>;
34
35
  beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
35
36
  beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
36
37
  beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
package/dist/index.js CHANGED
@@ -1028,8 +1028,8 @@ async function buildRoutes(server, applicationConfig) {
1028
1028
  routerContext,
1029
1029
  next,
1030
1030
  server,
1031
- applicationConfig,
1032
1031
  input,
1032
+ results: [],
1033
1033
  };
1034
1034
  await server.beforeRunRouteActions(handlerContext);
1035
1035
  let handler = routeConfig.handler;
@@ -2600,8 +2600,8 @@ async function findEntities(server, dataAccessor, options) {
2600
2600
  }
2601
2601
  }
2602
2602
  });
2603
- // if `keepNonPropertyFields` is true and `properties` are not specified, then select relation columns automatically.
2604
- if (options.keepNonPropertyFields && (!options.properties || !options.properties.length)) {
2603
+ // if `keepNonPropertyFields` is true, then select relation columns automatically.
2604
+ if (options.keepNonPropertyFields) {
2605
2605
  const oneRelationPropertiesWithNoLinkTable = getEntityPropertiesIncludingBase(server, model).filter((property) => property.relation === "one" && !property.linkTableName);
2606
2606
  oneRelationPropertiesWithNoLinkTable.forEach((property) => {
2607
2607
  if (property.targetIdColumnName) {
@@ -2675,12 +2675,12 @@ async function findEntities(server, dataAccessor, options) {
2675
2675
  return lodash.find(relationLinks, (link) => {
2676
2676
  return link[relationProperty.selfIdColumnName] == row["id"] && link[relationProperty.targetIdColumnName] == targetEntity["id"];
2677
2677
  });
2678
- }).map((targetEntity) => mapDbRowToEntity(server, relationModel, targetEntity, options.keepNonPropertyFields));
2678
+ });
2679
2679
  }
2680
2680
  else {
2681
2681
  row[relationProperty.code] = lodash.filter(relationLinks, (link) => {
2682
2682
  return link[relationProperty.selfIdColumnName] == row["id"];
2683
- }).map((link) => mapDbRowToEntity(server, relationModel, link.targetEntity, options.keepNonPropertyFields));
2683
+ }).map((link) => link.targetEntity);
2684
2684
  }
2685
2685
  });
2686
2686
  }
@@ -2708,20 +2708,20 @@ async function findEntities(server, dataAccessor, options) {
2708
2708
  selectRelationOptions: relationOptions[relationProperty.code],
2709
2709
  });
2710
2710
  }
2711
- const targetModel = server.getModel({
2711
+ server.getModel({
2712
2712
  singularCode: relationProperty.targetSingularCode,
2713
2713
  });
2714
2714
  rows.forEach((row) => {
2715
2715
  if (isManyRelation) {
2716
2716
  row[relationProperty.code] = lodash.filter(relatedEntities, (relatedEntity) => {
2717
2717
  return relatedEntity[relationProperty.selfIdColumnName] == row.id;
2718
- }).map((item) => mapDbRowToEntity(server, targetModel, item, options.keepNonPropertyFields));
2718
+ });
2719
2719
  }
2720
2720
  else {
2721
- row[relationProperty.code] = mapDbRowToEntity(server, targetModel, lodash.find(relatedEntities, (relatedEntity) => {
2721
+ row[relationProperty.code] = lodash.find(relatedEntities, (relatedEntity) => {
2722
2722
  // TODO: id property code should be configurable.
2723
2723
  return relatedEntity["id"] == row[relationProperty.targetIdColumnName];
2724
- }), options.keepNonPropertyFields);
2724
+ });
2725
2725
  }
2726
2726
  });
2727
2727
  }
@@ -4117,14 +4117,51 @@ var healthz = {
4117
4117
  type: "RESTful",
4118
4118
  method: "GET",
4119
4119
  endpoint: "/healthz",
4120
- handler: handler$x,
4120
+ handler: handler$y,
4121
4121
  };
4122
- async function handler$x(ctx) {
4122
+ async function handler$y(ctx) {
4123
4123
  ctx.output = {};
4124
4124
  }
4125
4125
 
4126
4126
  var coreRoutes = [healthz];
4127
4127
 
4128
+ const genExpression = lodash.memoize((varNames, expressionString) => {
4129
+ // eslint-disable-next-line no-new-func
4130
+ return new Function(...varNames, `return (${expressionString})`);
4131
+ }, (varNames, expressionString) => {
4132
+ return varNames.join(",") + ":" + expressionString;
4133
+ });
4134
+ function interpreteExpression(expressionString, rootVars) {
4135
+ const varNames = [];
4136
+ const varValues = [];
4137
+ for (const name in rootVars) {
4138
+ varNames.push(name);
4139
+ varValues.push(rootVars[name]);
4140
+ }
4141
+ let result;
4142
+ try {
4143
+ const expression = genExpression(varNames, expressionString);
4144
+ result = expression(...varValues);
4145
+ }
4146
+ catch (err) {
4147
+ console.error(`Expression interprete error. expression: '${expressionString}', error:`, err.message);
4148
+ }
4149
+ return result;
4150
+ }
4151
+ function interpreteConfigExpressions(config, vars) {
4152
+ if (!config) {
4153
+ return;
4154
+ }
4155
+ const propExpressions = config.$exps;
4156
+ if (!propExpressions) {
4157
+ return;
4158
+ }
4159
+ for (const propName in propExpressions) {
4160
+ const propValue = interpreteExpression(propExpressions[propName], vars);
4161
+ lodash.set(config, propName, propValue);
4162
+ }
4163
+ }
4164
+
4128
4165
  class RapidServer {
4129
4166
  #logger;
4130
4167
  #facilityFactories;
@@ -4491,6 +4528,22 @@ class RapidServer {
4491
4528
  }
4492
4529
  return response.getResponse();
4493
4530
  }
4531
+ async runActionHandlers(handlerContext, actions) {
4532
+ if (!actions) {
4533
+ return;
4534
+ }
4535
+ for (const action of actions) {
4536
+ const actionCode = action.code;
4537
+ interpreteConfigExpressions(action.config, handlerContext.vars);
4538
+ const handler = this.getActionHandlerByCode(actionCode);
4539
+ if (!handler) {
4540
+ throw new Error("Unknown handler: " + actionCode);
4541
+ }
4542
+ await this.beforeRunActionHandler(handlerContext, action);
4543
+ const result = await handler(handlerContext, action.config);
4544
+ handlerContext.results.push(result);
4545
+ }
4546
+ }
4494
4547
  async beforeRunRouteActions(handlerContext) {
4495
4548
  await this.#pluginManager.beforeRunRouteActions(handlerContext);
4496
4549
  }
@@ -5498,32 +5551,34 @@ class CacheFactory {
5498
5551
  }
5499
5552
  }
5500
5553
 
5501
- const code$w = "listMetaModels";
5502
- async function handler$w(plugin, ctx, options) {
5503
- const { applicationConfig } = ctx;
5554
+ const code$x = "listMetaModels";
5555
+ async function handler$x(plugin, ctx, options) {
5556
+ const { server } = ctx;
5557
+ const applicationConfig = server.getApplicationConfig();
5504
5558
  ctx.output = { list: applicationConfig.models };
5505
5559
  }
5506
5560
 
5507
5561
  var listMetaModels = /*#__PURE__*/Object.freeze({
5508
5562
  __proto__: null,
5509
- code: code$w,
5510
- handler: handler$w
5563
+ code: code$x,
5564
+ handler: handler$x
5511
5565
  });
5512
5566
 
5513
- const code$v = "listMetaRoutes";
5514
- async function handler$v(plugin, ctx, options) {
5515
- const { applicationConfig } = ctx;
5567
+ const code$w = "listMetaRoutes";
5568
+ async function handler$w(plugin, ctx, options) {
5569
+ const { server } = ctx;
5570
+ const applicationConfig = server.getApplicationConfig();
5516
5571
  ctx.output = { list: applicationConfig.routes };
5517
5572
  }
5518
5573
 
5519
5574
  var listMetaRoutes = /*#__PURE__*/Object.freeze({
5520
5575
  __proto__: null,
5521
- code: code$v,
5522
- handler: handler$v
5576
+ code: code$w,
5577
+ handler: handler$w
5523
5578
  });
5524
5579
 
5525
- const code$u = "getMetaModelDetail";
5526
- async function handler$u(plugin, ctx, options) {
5580
+ const code$v = "getMetaModelDetail";
5581
+ async function handler$v(plugin, ctx, options) {
5527
5582
  const { server, input } = ctx;
5528
5583
  const model = server.getModel(input);
5529
5584
  ctx.output = model;
@@ -5531,8 +5586,8 @@ async function handler$u(plugin, ctx, options) {
5531
5586
 
5532
5587
  var getMetaModelDetail = /*#__PURE__*/Object.freeze({
5533
5588
  __proto__: null,
5534
- code: code$u,
5535
- handler: handler$u
5589
+ code: code$v,
5590
+ handler: handler$v
5536
5591
  });
5537
5592
 
5538
5593
  function removeFiltersWithNullValue(filters) {
@@ -6121,9 +6176,9 @@ async function runCollectionEntityActionHandler(ctx, options, code, autoMergeInp
6121
6176
  }
6122
6177
  }
6123
6178
 
6124
- const code$t = "findCollectionEntities";
6125
- async function handler$t(plugin, ctx, options) {
6126
- await runCollectionEntityActionHandler(ctx, options, code$t, true, false, async (entityManager, input) => {
6179
+ const code$u = "findCollectionEntities";
6180
+ async function handler$u(plugin, ctx, options) {
6181
+ await runCollectionEntityActionHandler(ctx, options, code$u, true, false, async (entityManager, input) => {
6127
6182
  const { routerContext: routeContext } = ctx;
6128
6183
  input.filters = removeFiltersWithNullValue(input.filters);
6129
6184
  input.routeContext = routeContext;
@@ -6140,13 +6195,13 @@ async function handler$t(plugin, ctx, options) {
6140
6195
 
6141
6196
  var findCollectionEntities = /*#__PURE__*/Object.freeze({
6142
6197
  __proto__: null,
6143
- code: code$t,
6144
- handler: handler$t
6198
+ code: code$u,
6199
+ handler: handler$u
6145
6200
  });
6146
6201
 
6147
- const code$s = "findCollectionEntityById";
6148
- async function handler$s(plugin, ctx, options) {
6149
- await runCollectionEntityActionHandler(ctx, options, code$s, true, true, async (entityManager, input) => {
6202
+ const code$t = "findCollectionEntityById";
6203
+ async function handler$t(plugin, ctx, options) {
6204
+ await runCollectionEntityActionHandler(ctx, options, code$t, true, true, async (entityManager, input) => {
6150
6205
  const { routerContext: routeContext } = ctx;
6151
6206
  const { id } = input;
6152
6207
  const entity = await entityManager.findById({
@@ -6168,13 +6223,13 @@ async function handler$s(plugin, ctx, options) {
6168
6223
 
6169
6224
  var findCollectionEntityById = /*#__PURE__*/Object.freeze({
6170
6225
  __proto__: null,
6171
- code: code$s,
6172
- handler: handler$s
6226
+ code: code$t,
6227
+ handler: handler$t
6173
6228
  });
6174
6229
 
6175
- const code$r = "countCollectionEntities";
6176
- async function handler$r(plugin, ctx, options) {
6177
- await runCollectionEntityActionHandler(ctx, options, code$r, true, false, async (entityManager, input) => {
6230
+ const code$s = "countCollectionEntities";
6231
+ async function handler$s(plugin, ctx, options) {
6232
+ await runCollectionEntityActionHandler(ctx, options, code$s, true, false, async (entityManager, input) => {
6178
6233
  const { routerContext: routeContext } = ctx;
6179
6234
  input.filters = removeFiltersWithNullValue(input.filters);
6180
6235
  input.routeContext = routeContext;
@@ -6185,13 +6240,13 @@ async function handler$r(plugin, ctx, options) {
6185
6240
 
6186
6241
  var countCollectionEntities = /*#__PURE__*/Object.freeze({
6187
6242
  __proto__: null,
6188
- code: code$r,
6189
- handler: handler$r
6243
+ code: code$s,
6244
+ handler: handler$s
6190
6245
  });
6191
6246
 
6192
- const code$q = "createCollectionEntity";
6193
- async function handler$q(plugin, ctx, options) {
6194
- await runCollectionEntityActionHandler(ctx, options, code$q, true, true, async (entityManager, input) => {
6247
+ const code$r = "createCollectionEntity";
6248
+ async function handler$r(plugin, ctx, options) {
6249
+ await runCollectionEntityActionHandler(ctx, options, code$r, true, true, async (entityManager, input) => {
6195
6250
  const { routerContext: routeContext } = ctx;
6196
6251
  const output = await entityManager.createEntity({
6197
6252
  entity: input,
@@ -6203,16 +6258,16 @@ async function handler$q(plugin, ctx, options) {
6203
6258
 
6204
6259
  var createCollectionEntity = /*#__PURE__*/Object.freeze({
6205
6260
  __proto__: null,
6206
- code: code$q,
6207
- handler: handler$q
6261
+ code: code$r,
6262
+ handler: handler$r
6208
6263
  });
6209
6264
 
6210
- const code$p = "createCollectionEntitiesBatch";
6211
- async function handler$p(plugin, ctx, options) {
6265
+ const code$q = "createCollectionEntitiesBatch";
6266
+ async function handler$q(plugin, ctx, options) {
6212
6267
  const { input } = ctx;
6213
6268
  const { noTransaction } = input;
6214
6269
  const { defaultInput, fixedInput } = options;
6215
- await runCollectionEntityActionHandler(ctx, options, code$p, false, !noTransaction, async (entityManager, input) => {
6270
+ await runCollectionEntityActionHandler(ctx, options, code$q, false, !noTransaction, async (entityManager, input) => {
6216
6271
  const { routerContext: routeContext } = ctx;
6217
6272
  const { entities } = input;
6218
6273
  if (!lodash.isArray(entities)) {
@@ -6250,13 +6305,13 @@ async function createEntities(options) {
6250
6305
 
6251
6306
  var createCollectionEntitiesBatch = /*#__PURE__*/Object.freeze({
6252
6307
  __proto__: null,
6253
- code: code$p,
6254
- handler: handler$p
6308
+ code: code$q,
6309
+ handler: handler$q
6255
6310
  });
6256
6311
 
6257
- const code$o = "updateCollectionEntityById";
6258
- async function handler$o(plugin, ctx, options) {
6259
- await runCollectionEntityActionHandler(ctx, options, code$o, true, true, async (entityManager, input) => {
6312
+ const code$p = "updateCollectionEntityById";
6313
+ async function handler$p(plugin, ctx, options) {
6314
+ await runCollectionEntityActionHandler(ctx, options, code$p, true, true, async (entityManager, input) => {
6260
6315
  const { routerContext: routeContext } = ctx;
6261
6316
  const operation = input.$operation;
6262
6317
  if (operation) {
@@ -6285,15 +6340,15 @@ async function handler$o(plugin, ctx, options) {
6285
6340
 
6286
6341
  var updateCollectionEntityById = /*#__PURE__*/Object.freeze({
6287
6342
  __proto__: null,
6288
- code: code$o,
6289
- handler: handler$o
6343
+ code: code$p,
6344
+ handler: handler$p
6290
6345
  });
6291
6346
 
6292
- const code$n = "deleteCollectionEntities";
6293
- async function handler$n(plugin, ctx, options) {
6347
+ const code$o = "deleteCollectionEntities";
6348
+ async function handler$o(plugin, ctx, options) {
6294
6349
  const { input } = ctx;
6295
6350
  const { noTransaction } = input;
6296
- await runCollectionEntityActionHandler(ctx, options, code$n, true, !noTransaction, async (entityManager, input) => {
6351
+ await runCollectionEntityActionHandler(ctx, options, code$o, true, !noTransaction, async (entityManager, input) => {
6297
6352
  const { routerContext: routeContext } = ctx;
6298
6353
  const { filters } = input;
6299
6354
  if (!filters || !filters.length) {
@@ -6315,13 +6370,13 @@ async function handler$n(plugin, ctx, options) {
6315
6370
 
6316
6371
  var deleteCollectionEntities = /*#__PURE__*/Object.freeze({
6317
6372
  __proto__: null,
6318
- code: code$n,
6319
- handler: handler$n
6373
+ code: code$o,
6374
+ handler: handler$o
6320
6375
  });
6321
6376
 
6322
- const code$m = "deleteCollectionEntityById";
6323
- async function handler$m(plugin, ctx, options) {
6324
- await runCollectionEntityActionHandler(ctx, options, code$m, true, true, async (entityManager, input) => {
6377
+ const code$n = "deleteCollectionEntityById";
6378
+ async function handler$n(plugin, ctx, options) {
6379
+ await runCollectionEntityActionHandler(ctx, options, code$n, true, true, async (entityManager, input) => {
6325
6380
  const { routerContext: routeContext } = ctx;
6326
6381
  await entityManager.deleteById({
6327
6382
  id: input.id,
@@ -6333,13 +6388,13 @@ async function handler$m(plugin, ctx, options) {
6333
6388
 
6334
6389
  var deleteCollectionEntityById = /*#__PURE__*/Object.freeze({
6335
6390
  __proto__: null,
6336
- code: code$m,
6337
- handler: handler$m
6391
+ code: code$n,
6392
+ handler: handler$n
6338
6393
  });
6339
6394
 
6340
- const code$l = "addEntityRelations";
6341
- async function handler$l(plugin, ctx, options) {
6342
- await runCollectionEntityActionHandler(ctx, options, code$l, true, true, async (entityManager, input) => {
6395
+ const code$m = "addEntityRelations";
6396
+ async function handler$m(plugin, ctx, options) {
6397
+ await runCollectionEntityActionHandler(ctx, options, code$m, true, true, async (entityManager, input) => {
6343
6398
  const { routerContext: routeContext } = ctx;
6344
6399
  input.routeContext = routeContext;
6345
6400
  await entityManager.addRelations(input, plugin);
@@ -6349,13 +6404,13 @@ async function handler$l(plugin, ctx, options) {
6349
6404
 
6350
6405
  var addEntityRelations = /*#__PURE__*/Object.freeze({
6351
6406
  __proto__: null,
6352
- code: code$l,
6353
- handler: handler$l
6407
+ code: code$m,
6408
+ handler: handler$m
6354
6409
  });
6355
6410
 
6356
- const code$k = "removeEntityRelations";
6357
- async function handler$k(plugin, ctx, options) {
6358
- await runCollectionEntityActionHandler(ctx, options, code$k, true, true, async (entityManager, input) => {
6411
+ const code$l = "removeEntityRelations";
6412
+ async function handler$l(plugin, ctx, options) {
6413
+ await runCollectionEntityActionHandler(ctx, options, code$l, true, true, async (entityManager, input) => {
6359
6414
  const { routerContext: routeContext } = ctx;
6360
6415
  input.routeContext = routeContext;
6361
6416
  await entityManager.removeRelations(input, plugin);
@@ -6364,6 +6419,41 @@ async function handler$k(plugin, ctx, options) {
6364
6419
  }
6365
6420
 
6366
6421
  var removeEntityRelations = /*#__PURE__*/Object.freeze({
6422
+ __proto__: null,
6423
+ code: code$l,
6424
+ handler: handler$l
6425
+ });
6426
+
6427
+ const code$k = "saveEntity";
6428
+ async function handler$k(plugin, ctx, options) {
6429
+ const { server, routerContext: routeContext, vars } = ctx;
6430
+ interpreteConfigExpressions(options, vars || {});
6431
+ const input = options.entity;
6432
+ const operation = input.$operation;
6433
+ if (operation) {
6434
+ delete input.$operation;
6435
+ }
6436
+ const stateProperties = input.$stateProperties;
6437
+ if (stateProperties) {
6438
+ delete input.$stateProperties;
6439
+ }
6440
+ const relationPropertiesToUpdate = input.$relationPropertiesToUpdate;
6441
+ if (relationPropertiesToUpdate) {
6442
+ delete input.$relationPropertiesToUpdate;
6443
+ }
6444
+ const entityManager = server.getEntityManager(options.singularCode);
6445
+ const updateEntityByIdOptions = {
6446
+ id: input.id,
6447
+ entityToSave: input,
6448
+ operation,
6449
+ stateProperties,
6450
+ relationPropertiesToUpdate,
6451
+ routeContext,
6452
+ };
6453
+ ctx.output = await entityManager.updateEntityById(updateEntityByIdOptions, plugin);
6454
+ }
6455
+
6456
+ var saveEntity = /*#__PURE__*/Object.freeze({
6367
6457
  __proto__: null,
6368
6458
  code: code$k,
6369
6459
  handler: handler$k
@@ -6484,6 +6574,7 @@ class DataManager {
6484
6574
  server.registerActionHandler(this, removeEntityRelations);
6485
6575
  server.registerActionHandler(this, deleteCollectionEntities);
6486
6576
  server.registerActionHandler(this, deleteCollectionEntityById);
6577
+ server.registerActionHandler(this, saveEntity);
6487
6578
  server.registerActionHandler(this, queryDatabase);
6488
6579
  }
6489
6580
  async configureRoutes(server, applicationConfig) {
@@ -7679,7 +7770,7 @@ function getFileBaseName(pathname) {
7679
7770
 
7680
7771
  const code$a = "downloadDocument";
7681
7772
  async function handler$a(plugin, ctx, options) {
7682
- const { server, applicationConfig, routerContext: routeContext, input } = ctx;
7773
+ const { server, routerContext: routeContext, input } = ctx;
7683
7774
  const { request, response } = routeContext;
7684
7775
  const documentDataAccessor = ctx.server.getDataAccessor({
7685
7776
  singularCode: "ecm_document",
@@ -7740,7 +7831,7 @@ var downloadDocumentActionHandler = /*#__PURE__*/Object.freeze({
7740
7831
 
7741
7832
  const code$9 = "downloadFile";
7742
7833
  async function handler$9(plugin, ctx, options) {
7743
- const { server, applicationConfig, routerContext: routeContext } = ctx;
7834
+ const { server, routerContext: routeContext } = ctx;
7744
7835
  const { request, response } = routeContext;
7745
7836
  //TODO: only public files can download by this handler
7746
7837
  const input = ctx.input;
@@ -7771,7 +7862,7 @@ var downloadFileActionHandler = /*#__PURE__*/Object.freeze({
7771
7862
 
7772
7863
  const code$8 = "uploadFile";
7773
7864
  async function handler$8(plugin, ctx, options) {
7774
- const { server, applicationConfig, routerContext, input } = ctx;
7865
+ const { server, routerContext, input } = ctx;
7775
7866
  let file = input.file || input.files;
7776
7867
  if (lodash.isArray(file)) {
7777
7868
  file = file[0];
@@ -9397,10 +9488,9 @@ class CronJobService {
9397
9488
  let handlerContext = {
9398
9489
  logger,
9399
9490
  routerContext: RouteContext.newSystemOperationContext(server),
9400
- next: null,
9401
9491
  server,
9402
- applicationConfig: null,
9403
9492
  input,
9493
+ results: [],
9404
9494
  };
9405
9495
  let result;
9406
9496
  let lastErrorMessage;
@@ -0,0 +1,8 @@
1
+ import { ActionHandlerContext } from "../../../core/actionHandler";
2
+ import { RapidPlugin } from "../../../core/server";
3
+ export declare const code = "saveEntity";
4
+ export type SaveEntityActionHandlerOptions = {
5
+ singularCode: string;
6
+ entity?: Record<string, any>;
7
+ };
8
+ export declare function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: SaveEntityActionHandlerOptions): Promise<void>;
package/dist/server.d.ts CHANGED
@@ -57,6 +57,7 @@ export declare class RapidServer implements IRpdServer {
57
57
  tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient, dropErrorLog?: boolean): Promise<any[]>;
58
58
  get middlewares(): any[];
59
59
  handleRequest(rapidRequest: RapidRequest, next: Next): Promise<Response>;
60
+ runActionHandlers(handlerContext: ActionHandlerContext, actions: RpdRouteActionConfig[]): Promise<void>;
60
61
  beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
61
62
  beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
62
63
  beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
package/dist/types.d.ts CHANGED
@@ -29,6 +29,9 @@ export interface IDatabaseAccessor {
29
29
  getClient(): Promise<IDatabaseClient>;
30
30
  queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
31
31
  }
32
+ export type RunActionHandlersOptions = {
33
+ actions: RpdRouteActionConfig[];
34
+ };
32
35
  export interface RunEntityActionHandlerOptions {
33
36
  /** 模型所在的命名空间 */
34
37
  namespace: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.10.4",
3
+ "version": "0.10.6",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,45 @@
1
+ import { memoize, set } from "lodash";
2
+
3
+ const genExpression = memoize(
4
+ (varNames: string[], expressionString: string) => {
5
+ // eslint-disable-next-line no-new-func
6
+ return new Function(...varNames, `return (${expressionString})`);
7
+ },
8
+ (varNames, expressionString) => {
9
+ return varNames.join(",") + ":" + expressionString;
10
+ },
11
+ );
12
+
13
+ export function interpreteExpression(expressionString: string, rootVars: Record<string, any>) {
14
+ const varNames = [];
15
+ const varValues = [];
16
+ for (const name in rootVars) {
17
+ varNames.push(name);
18
+ varValues.push(rootVars[name]);
19
+ }
20
+
21
+ let result;
22
+ try {
23
+ const expression = genExpression(varNames, expressionString);
24
+ result = expression(...varValues);
25
+ } catch (err: any) {
26
+ console.error(`Expression interprete error. expression: '${expressionString}', error:`, err.message);
27
+ }
28
+ return result;
29
+ }
30
+
31
+ export function interpreteConfigExpressions(config: Record<string, any>, vars: Record<string, any>) {
32
+ if (!config) {
33
+ return;
34
+ }
35
+
36
+ const propExpressions = config.$exps;
37
+ if (!propExpressions) {
38
+ return;
39
+ }
40
+
41
+ for (const propName in propExpressions) {
42
+ const propValue = interpreteExpression(propExpressions[propName], vars);
43
+ set(config, propName, propValue);
44
+ }
45
+ }
@@ -7,11 +7,12 @@ import { CronJobConfiguration } from "~/types/cron-job-types";
7
7
  export interface ActionHandlerContext {
8
8
  logger: Logger;
9
9
  routerContext: RouteContext;
10
- next: Next;
10
+ next?: Next;
11
11
  server: IRpdServer;
12
- applicationConfig: RpdApplicationConfig;
12
+ vars?: Record<string, any>;
13
13
  input?: any;
14
14
  output?: any;
15
+ results: any[];
15
16
  status?: Response["status"];
16
17
  }
17
18
 
@@ -61,8 +61,8 @@ export async function buildRoutes(server: IRpdServer, applicationConfig: RpdAppl
61
61
  routerContext,
62
62
  next,
63
63
  server,
64
- applicationConfig,
65
64
  input,
65
+ results: [],
66
66
  };
67
67
 
68
68
  await server.beforeRunRouteActions(handlerContext);
@@ -15,6 +15,7 @@ import {
15
15
  RpdDataModelProperty,
16
16
  RpdRouteActionConfig,
17
17
  RpdServerEventTypes,
18
+ RunActionHandlersOptions,
18
19
  UpdateEntityByIdOptions,
19
20
  } from "~/types";
20
21
  import { IPluginActionHandler, ActionHandler, ActionHandlerContext } from "./actionHandler";
@@ -53,6 +54,7 @@ export interface IRpdServer {
53
54
  registerEntityWatcher(entityWatcher: EntityWatcherType);
54
55
  emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>): void;
55
56
  handleRequest(request: RapidRequest, next: Next): Promise<Response>;
57
+ runActionHandlers(handlerContext: ActionHandlerContext, actions: RpdRouteActionConfig[]): Promise<void>;
56
58
  beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
57
59
  beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
58
60
  beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
@@ -208,8 +208,8 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
208
208
  }
209
209
  });
210
210
 
211
- // if `keepNonPropertyFields` is true and `properties` are not specified, then select relation columns automatically.
212
- if (options.keepNonPropertyFields && (!options.properties || !options.properties.length)) {
211
+ // if `keepNonPropertyFields` is true, then select relation columns automatically.
212
+ if (options.keepNonPropertyFields) {
213
213
  const oneRelationPropertiesWithNoLinkTable = getEntityPropertiesIncludingBase(server, model).filter(
214
214
  (property) => property.relation === "one" && !property.linkTableName,
215
215
  );
@@ -289,11 +289,11 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
289
289
  return find(relationLinks, (link: any) => {
290
290
  return link[relationProperty.selfIdColumnName!] == row["id"] && link[relationProperty.targetIdColumnName!] == targetEntity["id"];
291
291
  });
292
- }).map((targetEntity) => mapDbRowToEntity(server, relationModel, targetEntity, options.keepNonPropertyFields));
292
+ });
293
293
  } else {
294
294
  row[relationProperty.code] = filter(relationLinks, (link: any) => {
295
295
  return link[relationProperty.selfIdColumnName!] == row["id"];
296
- }).map((link) => mapDbRowToEntity(server, relationModel, link.targetEntity, options.keepNonPropertyFields));
296
+ }).map((link) => link.targetEntity);
297
297
  }
298
298
  });
299
299
  }
@@ -332,17 +332,12 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
332
332
  if (isManyRelation) {
333
333
  row[relationProperty.code] = filter(relatedEntities, (relatedEntity: any) => {
334
334
  return relatedEntity[relationProperty.selfIdColumnName!] == row.id;
335
- }).map((item) => mapDbRowToEntity(server, targetModel!, item, options.keepNonPropertyFields));
335
+ });
336
336
  } else {
337
- row[relationProperty.code] = mapDbRowToEntity(
338
- server,
339
- targetModel!,
340
- find(relatedEntities, (relatedEntity: any) => {
341
- // TODO: id property code should be configurable.
342
- return relatedEntity["id"] == row[relationProperty.targetIdColumnName!];
343
- }),
344
- options.keepNonPropertyFields,
345
- );
337
+ row[relationProperty.code] = find(relatedEntities, (relatedEntity: any) => {
338
+ // TODO: id property code should be configurable.
339
+ return relatedEntity["id"] == row[relationProperty.targetIdColumnName!];
340
+ });
346
341
  }
347
342
  });
348
343
  }
@@ -133,10 +133,9 @@ export default class CronJobService {
133
133
  let handlerContext: ActionHandlerContext = {
134
134
  logger,
135
135
  routerContext: RouteContext.newSystemOperationContext(server),
136
- next: null,
137
136
  server,
138
- applicationConfig: null,
139
137
  input,
138
+ results: [],
140
139
  };
141
140
 
142
141
  let result: JobRunningResult;
@@ -16,6 +16,7 @@ import * as deleteCollectionEntities from "./actionHandlers/deleteCollectionEnti
16
16
  import * as deleteCollectionEntityById from "./actionHandlers/deleteCollectionEntityById";
17
17
  import * as addEntityRelations from "./actionHandlers/addEntityRelations";
18
18
  import * as removeEntityRelations from "./actionHandlers/removeEntityRelations";
19
+ import * as saveEntity from "./actionHandlers/saveEntity";
19
20
  import * as queryDatabase from "./actionHandlers/queryDatabase";
20
21
  import {
21
22
  RpdServerPluginExtendingAbilities,
@@ -125,6 +126,7 @@ class DataManager implements RapidPlugin {
125
126
  server.registerActionHandler(this, removeEntityRelations);
126
127
  server.registerActionHandler(this, deleteCollectionEntities);
127
128
  server.registerActionHandler(this, deleteCollectionEntityById);
129
+ server.registerActionHandler(this, saveEntity);
128
130
  server.registerActionHandler(this, queryDatabase);
129
131
  }
130
132
 
@@ -0,0 +1,46 @@
1
+ import { RunEntityActionHandlerOptions, UpdateEntityByIdOptions } from "~/types";
2
+ import { ActionHandlerContext } from "~/core/actionHandler";
3
+ import { RapidPlugin } from "~/core/server";
4
+ import runCollectionEntityActionHandler from "~/helpers/runCollectionEntityActionHandler";
5
+ import { interpreteConfigExpressions } from "~/core/ExpressionInterpreter";
6
+
7
+ export const code = "saveEntity";
8
+
9
+ export type SaveEntityActionHandlerOptions = {
10
+ singularCode: string;
11
+ entity?: Record<string, any>;
12
+ };
13
+
14
+ export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: SaveEntityActionHandlerOptions) {
15
+ const { server, routerContext: routeContext, vars } = ctx;
16
+
17
+ interpreteConfigExpressions(options, vars || {});
18
+
19
+ const input = options.entity;
20
+
21
+ const operation = input.$operation;
22
+ if (operation) {
23
+ delete input.$operation;
24
+ }
25
+
26
+ const stateProperties = input.$stateProperties;
27
+ if (stateProperties) {
28
+ delete input.$stateProperties;
29
+ }
30
+
31
+ const relationPropertiesToUpdate = input.$relationPropertiesToUpdate;
32
+ if (relationPropertiesToUpdate) {
33
+ delete input.$relationPropertiesToUpdate;
34
+ }
35
+
36
+ const entityManager = server.getEntityManager(options.singularCode);
37
+ const updateEntityByIdOptions: UpdateEntityByIdOptions = {
38
+ id: input.id,
39
+ entityToSave: input,
40
+ operation,
41
+ stateProperties,
42
+ relationPropertiesToUpdate,
43
+ routeContext,
44
+ };
45
+ ctx.output = await entityManager.updateEntityById(updateEntityByIdOptions, plugin);
46
+ }
@@ -7,7 +7,7 @@ import { getFileBaseName } from "~/utilities/pathUtility";
7
7
  export const code = "downloadDocument";
8
8
 
9
9
  export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
10
- const { server, applicationConfig, routerContext: routeContext, input } = ctx;
10
+ const { server, routerContext: routeContext, input } = ctx;
11
11
  const { request, response } = routeContext;
12
12
 
13
13
  const documentDataAccessor = ctx.server.getDataAccessor({
@@ -13,7 +13,7 @@ export type DownloadFileInput = {
13
13
  export const code = "downloadFile";
14
14
 
15
15
  export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
16
- const { server, applicationConfig, routerContext: routeContext } = ctx;
16
+ const { server, routerContext: routeContext } = ctx;
17
17
  const { request, response } = routeContext;
18
18
  //TODO: only public files can download by this handler
19
19
 
@@ -8,7 +8,7 @@ import { RapidPlugin } from "~/core/server";
8
8
  export const code = "uploadFile";
9
9
 
10
10
  export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
11
- const { server, applicationConfig, routerContext, input } = ctx;
11
+ const { server, routerContext, input } = ctx;
12
12
  const { request, response } = routerContext;
13
13
 
14
14
  let file: File | File[] | null = input.file || input.files;
@@ -4,6 +4,7 @@ import { RapidPlugin } from "~/core/server";
4
4
  export const code = "listMetaModels";
5
5
 
6
6
  export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
7
- const { applicationConfig } = ctx;
7
+ const { server } = ctx;
8
+ const applicationConfig = server.getApplicationConfig();
8
9
  ctx.output = { list: applicationConfig.models };
9
10
  }
@@ -4,6 +4,7 @@ import { RapidPlugin } from "~/core/server";
4
4
  export const code = "listMetaRoutes";
5
5
 
6
6
  export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
7
- const { applicationConfig } = ctx;
7
+ const { server } = ctx;
8
+ const applicationConfig = server.getApplicationConfig();
8
9
  ctx.output = { list: applicationConfig.routes };
9
10
  }
package/src/server.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  EmitServerEventOptions,
20
20
  IDatabaseClient,
21
21
  RpdRouteActionConfig,
22
+ RunActionHandlersOptions,
22
23
  } from "./types";
23
24
 
24
25
  import QueryBuilder from "./queryBuilder/queryBuilder";
@@ -36,6 +37,7 @@ import { Logger } from "./facilities/log/LogFacility";
36
37
  import { FacilityFactory } from "./core/facility";
37
38
  import { CronJobConfiguration } from "./types/cron-job-types";
38
39
  import coreRoutes from "./core/routes";
40
+ import { interpreteConfigExpressions } from "./core/ExpressionInterpreter";
39
41
 
40
42
  export interface InitServerOptions {
41
43
  logger: Logger;
@@ -474,6 +476,26 @@ export class RapidServer implements IRpdServer {
474
476
  return response.getResponse();
475
477
  }
476
478
 
479
+ async runActionHandlers(handlerContext: ActionHandlerContext, actions: RpdRouteActionConfig[]) {
480
+ if (!actions) {
481
+ return;
482
+ }
483
+ for (const action of actions) {
484
+ const actionCode = action.code;
485
+ interpreteConfigExpressions(action.config, handlerContext.vars);
486
+
487
+ const handler = this.getActionHandlerByCode(actionCode);
488
+ if (!handler) {
489
+ throw new Error("Unknown handler: " + actionCode);
490
+ }
491
+
492
+ await this.beforeRunActionHandler(handlerContext, action);
493
+
494
+ const result = await handler(handlerContext, action.config);
495
+ handlerContext.results.push(result);
496
+ }
497
+ }
498
+
477
499
  async beforeRunRouteActions(handlerContext: ActionHandlerContext) {
478
500
  await this.#pluginManager.beforeRunRouteActions(handlerContext);
479
501
  }
package/src/types.ts CHANGED
@@ -35,6 +35,10 @@ export interface IDatabaseAccessor {
35
35
  queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
36
36
  }
37
37
 
38
+ export type RunActionHandlersOptions = {
39
+ actions: RpdRouteActionConfig[];
40
+ };
41
+
38
42
  export interface RunEntityActionHandlerOptions {
39
43
  /** 模型所在的命名空间 */
40
44
  namespace: string;