@ruiapp/rapid-core 0.1.73 → 0.1.74

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.
@@ -2,6 +2,78 @@ declare const _default: {
2
2
  code: string;
3
3
  name: string;
4
4
  models: ({
5
+ maintainedBy: string;
6
+ namespace: string;
7
+ name: string;
8
+ singularCode: string;
9
+ pluralCode: string;
10
+ schema: string;
11
+ tableName: string;
12
+ properties: ({
13
+ name: string;
14
+ code: string;
15
+ columnName: string;
16
+ type: "integer";
17
+ required: true;
18
+ autoIncrement: true;
19
+ relation?: undefined;
20
+ targetSingularCode?: undefined;
21
+ selfIdColumnName?: undefined;
22
+ } | {
23
+ name: string;
24
+ code: string;
25
+ columnName: string;
26
+ type: "text";
27
+ required: true;
28
+ autoIncrement?: undefined;
29
+ relation?: undefined;
30
+ targetSingularCode?: undefined;
31
+ selfIdColumnName?: undefined;
32
+ } | {
33
+ name: string;
34
+ code: string;
35
+ columnName: string;
36
+ type: "text";
37
+ required: false;
38
+ autoIncrement?: undefined;
39
+ relation?: undefined;
40
+ targetSingularCode?: undefined;
41
+ selfIdColumnName?: undefined;
42
+ } | {
43
+ name: string;
44
+ code: string;
45
+ type: "relation[]";
46
+ relation: "many";
47
+ targetSingularCode: string;
48
+ selfIdColumnName: string;
49
+ columnName?: undefined;
50
+ required?: undefined;
51
+ autoIncrement?: undefined;
52
+ } | {
53
+ name: string;
54
+ code: string;
55
+ columnName: string;
56
+ type: "json";
57
+ required: false;
58
+ autoIncrement?: undefined;
59
+ relation?: undefined;
60
+ targetSingularCode?: undefined;
61
+ selfIdColumnName?: undefined;
62
+ })[];
63
+ indexes: ({
64
+ name: string;
65
+ properties: {
66
+ code: string;
67
+ }[];
68
+ unique: true;
69
+ } | {
70
+ name: string;
71
+ properties: {
72
+ code: string;
73
+ }[];
74
+ unique: false;
75
+ })[];
76
+ } | {
5
77
  maintainedBy: string;
6
78
  namespace: string;
7
79
  name: string;
@@ -87,6 +159,13 @@ declare const _default: {
87
159
  targetIdColumnName?: undefined;
88
160
  defaultValue?: undefined;
89
161
  })[];
162
+ indexes: {
163
+ name: string;
164
+ properties: {
165
+ code: string;
166
+ }[];
167
+ unique: true;
168
+ }[];
90
169
  } | {
91
170
  maintainedBy: string;
92
171
  namespace: string;
@@ -162,6 +241,7 @@ declare const _default: {
162
241
  autoIncrement?: undefined;
163
242
  defaultValue?: undefined;
164
243
  })[];
244
+ indexes?: undefined;
165
245
  } | {
166
246
  maintainedBy: string;
167
247
  namespace: string;
@@ -237,6 +317,7 @@ declare const _default: {
237
317
  targetIdColumnName?: undefined;
238
318
  defaultValue?: undefined;
239
319
  })[];
320
+ indexes?: undefined;
240
321
  } | {
241
322
  maintainedBy: string;
242
323
  namespace: string;
@@ -274,6 +355,7 @@ declare const _default: {
274
355
  required: true;
275
356
  autoIncrement?: undefined;
276
357
  })[];
358
+ indexes?: undefined;
277
359
  })[];
278
360
  routes: {
279
361
  namespace: string;
@@ -1,5 +1,6 @@
1
- import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, FindEntitySelectRelationOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, RpdDataModelProperty, UpdateEntityByIdOptions } from "../types";
1
+ import { AddEntityRelationsOptions, CountEntityOptions, CountEntityResult, CreateEntityOptions, DeleteEntityByIdOptions, FindEntityByIdOptions, FindEntityOptions, FindEntitySelectRelationOptions, IRpdDataAccessor, RemoveEntityRelationsOptions, RpdDataModel, RpdDataModelIndex, RpdDataModelProperty, UpdateEntityByIdOptions } from "../types";
2
2
  import { IRpdServer, RapidPlugin } from "../core/server";
3
+ import { RouteContext } from "../core/routeContext";
3
4
  export type FindOneRelationEntitiesOptions = {
4
5
  server: IRpdServer;
5
6
  mainModel: RpdDataModel;
@@ -14,6 +15,12 @@ export type FindManyRelationEntitiesOptions = {
14
15
  mainEntityIds: any[];
15
16
  selectRelationOptions?: FindEntitySelectRelationOptions;
16
17
  };
18
+ export type CheckEntityDuplicatedOptions = {
19
+ routeContext?: RouteContext;
20
+ entityId?: number;
21
+ entityToSave: any;
22
+ indexConfig: RpdDataModelIndex;
23
+ };
17
24
  export default class EntityManager<TEntity = any> {
18
25
  #private;
19
26
  constructor(server: IRpdServer, dataAccessor: IRpdDataAccessor);
package/dist/index.js CHANGED
@@ -236,6 +236,7 @@ class QueryBuilder {
236
236
  builder: this,
237
237
  params: [],
238
238
  emitTableAlias: true,
239
+ paramToLiteral: false,
239
240
  };
240
241
  let { fields: columns, filters, orderBy, pagination } = options;
241
242
  let command = "SELECT ";
@@ -288,6 +289,7 @@ class QueryBuilder {
288
289
  builder: this,
289
290
  params: [],
290
291
  emitTableAlias: true,
292
+ paramToLiteral: false,
291
293
  };
292
294
  let { fields: columns, filters, orderBy, pagination } = options;
293
295
  let command = "SELECT ";
@@ -344,6 +346,7 @@ class QueryBuilder {
344
346
  builder: this,
345
347
  params: [],
346
348
  emitTableAlias: false,
349
+ paramToLiteral: false,
347
350
  };
348
351
  let { filters } = options;
349
352
  let command = 'SELECT COUNT(*)::int as "count" FROM ';
@@ -363,6 +366,7 @@ class QueryBuilder {
363
366
  builder: this,
364
367
  params: [],
365
368
  emitTableAlias: true,
369
+ paramToLiteral: false,
366
370
  };
367
371
  let { filters } = options;
368
372
  let command = 'SELECT COUNT(*)::int as "count" FROM ';
@@ -383,6 +387,7 @@ class QueryBuilder {
383
387
  builder: this,
384
388
  params,
385
389
  emitTableAlias: false,
390
+ paramToLiteral: false,
386
391
  };
387
392
  const { entity } = options;
388
393
  let command = "INSERT INTO ";
@@ -420,6 +425,7 @@ class QueryBuilder {
420
425
  builder: this,
421
426
  params,
422
427
  emitTableAlias: false,
428
+ paramToLiteral: false,
423
429
  };
424
430
  let { entity, filters } = options;
425
431
  let command = "UPDATE ";
@@ -460,6 +466,7 @@ class QueryBuilder {
460
466
  builder: this,
461
467
  params,
462
468
  emitTableAlias: false,
469
+ paramToLiteral: false,
463
470
  };
464
471
  let { filters } = options;
465
472
  let command = "DELETE FROM ";
@@ -473,6 +480,17 @@ class QueryBuilder {
473
480
  params: ctx.params,
474
481
  };
475
482
  }
483
+ buildFiltersExpression(model, filters) {
484
+ const params = [];
485
+ const ctx = {
486
+ model,
487
+ builder: this,
488
+ params,
489
+ emitTableAlias: false,
490
+ paramToLiteral: true,
491
+ };
492
+ return buildFiltersQuery(ctx, filters);
493
+ }
476
494
  }
477
495
  function buildFiltersQuery(ctx, filters) {
478
496
  return buildFilterQuery(0, ctx, {
@@ -548,58 +566,99 @@ function buildInFilterQuery(ctx, filter) {
548
566
  else {
549
567
  command += " <> ";
550
568
  }
551
- ctx.params.push(filter.value);
552
- command += `ANY($${ctx.params.length}::${filter.itemType || "int"}[])`;
569
+ if (ctx.paramToLiteral) ;
570
+ else {
571
+ ctx.params.push(filter.value);
572
+ command += `ANY($${ctx.params.length}::${filter.itemType || "int"}[])`;
573
+ }
553
574
  return command;
554
575
  }
555
576
  function buildContainsFilterQuery(ctx, filter) {
556
577
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
557
578
  command += " LIKE ";
558
- ctx.params.push(`%${filter.value}%`);
559
- command += "$" + ctx.params.length;
579
+ if (ctx.paramToLiteral) ;
580
+ else {
581
+ ctx.params.push(`%${filter.value}%`);
582
+ command += "$" + ctx.params.length;
583
+ }
560
584
  return command;
561
585
  }
562
586
  function buildNotContainsFilterQuery(ctx, filter) {
563
587
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
564
588
  command += " NOT LIKE ";
565
- ctx.params.push(`%${filter.value}%`);
566
- command += "$" + ctx.params.length;
589
+ if (ctx.paramToLiteral) ;
590
+ else {
591
+ ctx.params.push(`%${filter.value}%`);
592
+ command += "$" + ctx.params.length;
593
+ }
567
594
  return command;
568
595
  }
569
596
  function buildStartsWithFilterQuery(ctx, filter) {
570
597
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
571
598
  command += " LIKE ";
572
- ctx.params.push(`${filter.value}%`);
573
- command += "$" + ctx.params.length;
599
+ if (ctx.paramToLiteral) ;
600
+ else {
601
+ ctx.params.push(`${filter.value}%`);
602
+ command += "$" + ctx.params.length;
603
+ }
574
604
  return command;
575
605
  }
576
606
  function buildNotStartsWithFilterQuery(ctx, filter) {
577
607
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
578
608
  command += " NOT LIKE ";
579
- ctx.params.push(`${filter.value}%`);
580
- command += "$" + ctx.params.length;
609
+ if (ctx.paramToLiteral) ;
610
+ else {
611
+ ctx.params.push(`${filter.value}%`);
612
+ command += "$" + ctx.params.length;
613
+ }
581
614
  return command;
582
615
  }
583
616
  function buildEndsWithFilterQuery(ctx, filter) {
584
617
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
585
618
  command += " LIKE ";
586
- ctx.params.push(`%${filter.value}`);
587
- command += "$" + ctx.params.length;
619
+ if (ctx.paramToLiteral) ;
620
+ else {
621
+ ctx.params.push(`%${filter.value}`);
622
+ command += "$" + ctx.params.length;
623
+ }
588
624
  return command;
589
625
  }
590
626
  function buildNotEndsWithFilterQuery(ctx, filter) {
591
627
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
592
628
  command += " NOT LIKE ";
593
- ctx.params.push(`%${filter.value}`);
594
- command += "$" + ctx.params.length;
629
+ if (ctx.paramToLiteral) ;
630
+ else {
631
+ ctx.params.push(`%${filter.value}`);
632
+ command += "$" + ctx.params.length;
633
+ }
595
634
  return command;
596
635
  }
597
636
  function buildRelationalFilterQuery(ctx, filter) {
598
637
  let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
599
638
  command += relationalOperatorsMap.get(filter.operator);
600
- ctx.params.push(filter.value);
601
- command += "$" + ctx.params.length;
639
+ if (ctx.paramToLiteral) {
640
+ command += formatValueToSqlLiteral(filter.value);
641
+ }
642
+ else {
643
+ ctx.params.push(filter.value);
644
+ command += "$" + ctx.params.length;
645
+ }
602
646
  return command;
647
+ }
648
+ function formatValueToSqlLiteral(value) {
649
+ if (lodash.isNull(value) || lodash.isUndefined(value)) {
650
+ return "null";
651
+ }
652
+ if (lodash.isString(value)) {
653
+ return `'${value.replaceAll("'", "''")}'`;
654
+ }
655
+ if (lodash.isBoolean(value)) {
656
+ return value ? "true" : "false";
657
+ }
658
+ if (lodash.isNumber(value)) {
659
+ return value.toString();
660
+ }
661
+ return `'${value.toString().replaceAll("'", "''")}'`;
603
662
  }
604
663
 
605
664
  class PluginManager {
@@ -1392,6 +1451,42 @@ var bootstrapApplicationConfig = {
1392
1451
  targetSingularCode: "property",
1393
1452
  selfIdColumnName: "model_id",
1394
1453
  },
1454
+ {
1455
+ name: "indexes",
1456
+ code: "indexes",
1457
+ columnName: "indexes",
1458
+ type: "json",
1459
+ required: false,
1460
+ },
1461
+ ],
1462
+ indexes: [
1463
+ {
1464
+ name: "meta_models_singular_code_uindex",
1465
+ properties: [
1466
+ {
1467
+ code: "singularCode",
1468
+ },
1469
+ ],
1470
+ unique: true,
1471
+ },
1472
+ {
1473
+ name: "meta_models_table_name_uindex",
1474
+ properties: [
1475
+ {
1476
+ code: "tableName",
1477
+ },
1478
+ ],
1479
+ unique: true,
1480
+ },
1481
+ {
1482
+ name: "meta_models_name_index",
1483
+ properties: [
1484
+ {
1485
+ code: "name",
1486
+ },
1487
+ ],
1488
+ unique: false,
1489
+ },
1395
1490
  ],
1396
1491
  },
1397
1492
  {
@@ -1549,6 +1644,20 @@ var bootstrapApplicationConfig = {
1549
1644
  required: false,
1550
1645
  },
1551
1646
  ],
1647
+ indexes: [
1648
+ {
1649
+ name: "meta_properties_model_id_code_uindex",
1650
+ properties: [
1651
+ {
1652
+ code: "model",
1653
+ },
1654
+ {
1655
+ code: "code",
1656
+ },
1657
+ ],
1658
+ unique: true,
1659
+ },
1660
+ ],
1552
1661
  },
1553
1662
  {
1554
1663
  maintainedBy: "metaManager",
@@ -2672,6 +2781,24 @@ async function createEntity(server, dataAccessor, options, plugin) {
2672
2781
  sender: plugin,
2673
2782
  routeContext,
2674
2783
  });
2784
+ // check unique constraints
2785
+ if (!options.postponeUniquenessCheck) {
2786
+ if (model.indexes && model.indexes.length) {
2787
+ for (const indexConfig of model.indexes) {
2788
+ if (!indexConfig.unique) {
2789
+ continue;
2790
+ }
2791
+ const duplicate = await willEntityDuplicate(server, dataAccessor, {
2792
+ routeContext: options.routeContext,
2793
+ entityToSave: entity,
2794
+ indexConfig,
2795
+ });
2796
+ if (duplicate) {
2797
+ throw new Error(getEntityDuplicatedErrorMessage(server, model, indexConfig));
2798
+ }
2799
+ }
2800
+ }
2801
+ }
2675
2802
  const oneRelationPropertiesToCreate = [];
2676
2803
  const manyRelationPropertiesToCreate = [];
2677
2804
  lodash.keys(entity).forEach((propertyCode) => {
@@ -2884,6 +3011,25 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
2884
3011
  routeContext: options.routeContext,
2885
3012
  });
2886
3013
  changes = getEntityPartChanges(server, model, entity, entityToSave);
3014
+ // check unique constraints
3015
+ if (!options.postponeUniquenessCheck) {
3016
+ if (model.indexes && model.indexes.length) {
3017
+ for (const indexConfig of model.indexes) {
3018
+ if (!indexConfig.unique) {
3019
+ continue;
3020
+ }
3021
+ const duplicate = await willEntityDuplicate(server, dataAccessor, {
3022
+ routeContext: options.routeContext,
3023
+ entityId: id,
3024
+ entityToSave: changes,
3025
+ indexConfig,
3026
+ });
3027
+ if (duplicate) {
3028
+ throw new Error(getEntityDuplicatedErrorMessage(server, model, indexConfig));
3029
+ }
3030
+ }
3031
+ }
3032
+ }
2887
3033
  const oneRelationPropertiesToUpdate = [];
2888
3034
  const manyRelationPropertiesToUpdate = [];
2889
3035
  lodash.keys(changes).forEach((propertyCode) => {
@@ -3065,6 +3211,55 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
3065
3211
  });
3066
3212
  return updatedEntity;
3067
3213
  }
3214
+ async function willEntityDuplicate(server, dataAccessor, options) {
3215
+ const { entityId, entityToSave, routeContext, indexConfig } = options;
3216
+ let filters = [];
3217
+ if (indexConfig.conditions) {
3218
+ filters = lodash.cloneDeep(indexConfig.conditions);
3219
+ }
3220
+ for (const propConfig of indexConfig.properties) {
3221
+ let propCode;
3222
+ if (lodash.isString(propConfig)) {
3223
+ propCode = propConfig;
3224
+ }
3225
+ else {
3226
+ propCode = propConfig.code;
3227
+ }
3228
+ if (!entityToSave.hasOwnProperty(propCode)) {
3229
+ // skip duplicate checking when any index prop missing in entityToSave.
3230
+ return false;
3231
+ }
3232
+ filters.push({
3233
+ operator: "eq",
3234
+ field: propCode,
3235
+ value: entityToSave[propCode],
3236
+ });
3237
+ }
3238
+ const entityInDb = await findEntity(server, dataAccessor, {
3239
+ filters,
3240
+ routeContext,
3241
+ });
3242
+ if (entityId) {
3243
+ return entityInDb && entityInDb.Id !== entityId;
3244
+ }
3245
+ else {
3246
+ return !!entityInDb;
3247
+ }
3248
+ }
3249
+ function getEntityDuplicatedErrorMessage(server, model, indexConfig) {
3250
+ const propertyNames = indexConfig.properties.map((propConfig) => {
3251
+ let propCode;
3252
+ if (lodash.isString(propConfig)) {
3253
+ propCode = propConfig;
3254
+ }
3255
+ else {
3256
+ propCode = propConfig.code;
3257
+ }
3258
+ const prop = getEntityPropertyByCode(server, model, propCode);
3259
+ return prop.name;
3260
+ });
3261
+ return `已存在 ${propertyNames.join(", ")} 相同的记录。`;
3262
+ }
3068
3263
  class EntityManager {
3069
3264
  #server;
3070
3265
  #dataAccessor;
@@ -4056,6 +4251,16 @@ async function syncDatabaseSchema(server, applicationConfig) {
4056
4251
  await server.queryDatabaseObject(`ALTER TABLE ${queryBuilder.quoteTable(model)} ADD CONSTRAINT ${queryBuilder.quoteObject(expectedContraintName)} PRIMARY KEY (id);`, []);
4057
4252
  }
4058
4253
  }
4254
+ // generate indexes
4255
+ for (const model of applicationConfig.models) {
4256
+ if (!model.indexes || !model.indexes.length) {
4257
+ continue;
4258
+ }
4259
+ for (const index of model.indexes) {
4260
+ const sqlCreateIndex = generateTableIndexDDL(queryBuilder, server, model, index);
4261
+ await server.tryQueryDatabaseObject(sqlCreateIndex, []);
4262
+ }
4263
+ }
4059
4264
  }
4060
4265
  function generateCreateColumnDDL(queryBuilder, options) {
4061
4266
  let columnDDL = `ALTER TABLE ${queryBuilder.quoteTable(options)} ADD`;
@@ -4088,6 +4293,50 @@ function generateLinkTableDDL(queryBuilder, options) {
4088
4293
  columnDDL += `${queryBuilder.quoteObject(options.targetIdColumnName)} integer not null);`;
4089
4294
  return columnDDL;
4090
4295
  }
4296
+ function generateTableIndexDDL(queryBuilder, server, model, index) {
4297
+ let indexName = index.name;
4298
+ if (!indexName) {
4299
+ indexName = model.tableName;
4300
+ for (const indexProp of index.properties) {
4301
+ const propCode = lodash.isString(indexProp) ? indexProp : indexProp.code;
4302
+ const property = getEntityPropertyByCode(server, model, propCode);
4303
+ if (!isRelationProperty(property)) {
4304
+ indexName += "_" + property.columnName;
4305
+ }
4306
+ else if (isOneRelationProperty(property)) {
4307
+ indexName += "_" + property.targetIdColumnName;
4308
+ }
4309
+ }
4310
+ indexName += index.unique ? "_uindex" : "_index";
4311
+ }
4312
+ const indexColumns = lodash.map(index.properties, (indexProp) => {
4313
+ let columnName;
4314
+ const propCode = lodash.isString(indexProp) ? indexProp : indexProp.code;
4315
+ const property = getEntityPropertyByCode(server, model, propCode);
4316
+ if (!isRelationProperty(property)) {
4317
+ columnName = property.columnName;
4318
+ }
4319
+ else if (isOneRelationProperty(property)) {
4320
+ columnName = property.targetIdColumnName;
4321
+ }
4322
+ if (lodash.isString(indexProp)) {
4323
+ return columnName;
4324
+ }
4325
+ if (indexProp.order === "desc") {
4326
+ return `${columnName} desc`;
4327
+ }
4328
+ return columnName;
4329
+ });
4330
+ let ddl = `CREATE ${index.unique ? "UNIQUE" : ""} INDEX ${indexName} `;
4331
+ ddl += `ON ${queryBuilder.quoteTable({
4332
+ schema: model.schema,
4333
+ tableName: model.tableName,
4334
+ })} (${indexColumns.join(", ")})`;
4335
+ if (index.conditions) {
4336
+ ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, index.conditions)}`;
4337
+ }
4338
+ return ddl;
4339
+ }
4091
4340
  const pgPropertyTypeColumnMap = {
4092
4341
  integer: "int4",
4093
4342
  long: "int8",
@@ -5,6 +5,10 @@ export interface BuildQueryContext {
5
5
  builder: QueryBuilder;
6
6
  params: any[];
7
7
  emitTableAlias: boolean;
8
+ /**
9
+ * emit parameter value to sql literal.
10
+ */
11
+ paramToLiteral: boolean;
8
12
  }
9
13
  export interface InitQueryBuilderOptions {
10
14
  dbDefaultSchema: string;
@@ -22,5 +26,6 @@ export default class QueryBuilder {
22
26
  insert(model: RpdDataModel, options: CreateEntityOptions): DatabaseQuery;
23
27
  update(model: RpdDataModel, options: UpdateRowOptions): DatabaseQuery;
24
28
  delete(model: RpdDataModel, options: DeleteRowOptions): DatabaseQuery;
29
+ buildFiltersExpression(model: RpdDataModel, filters: RowFilterOptions[]): string;
25
30
  }
26
31
  export declare function buildFiltersQuery(ctx: BuildQueryContext, filters: RowFilterOptions[]): string;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RouteContext } from "./core/routeContext";
2
2
  import { IRpdServer, RapidPlugin } from "./core/server";
3
- import { ColumnSelectOptions, CountRowOptions, FindRowOptions } from "./dataAccess/dataAccessTypes";
3
+ import { ColumnSelectOptions, CountRowOptions, FindRowOptions, RowFilterOptions } from "./dataAccess/dataAccessTypes";
4
4
  export type RapidServerConfig = {
5
5
  baseUrl?: string;
6
6
  sessionCookieName: string;
@@ -148,6 +148,7 @@ export interface QuoteTableOptions {
148
148
  export interface IQueryBuilder {
149
149
  quoteTable: (options: QuoteTableOptions) => string;
150
150
  quoteObject: (name: string) => string;
151
+ buildFiltersExpression(model: RpdDataModel, filters: RowFilterOptions[]): any;
151
152
  }
152
153
  export interface RpdApplicationConfig {
153
154
  code?: string;
@@ -180,6 +181,7 @@ export interface RpdDataModel {
180
181
  */
181
182
  displayPropertyCode?: string;
182
183
  properties: RpdDataModelProperty[];
184
+ indexes?: RpdDataModelIndex[];
183
185
  extensions?: RpdDataModelExtension[];
184
186
  permissionPolicies?: RpdDataModelPermissionPolicies;
185
187
  }
@@ -320,6 +322,17 @@ export interface RpdDataModelExtension {
320
322
  code: string;
321
323
  config: any;
322
324
  }
325
+ export interface RpdDataModelIndex {
326
+ name?: string;
327
+ unique?: boolean;
328
+ properties: RpdDataModelIndexPropertyConfig[];
329
+ conditions?: RpdDataModelIndexOptions[];
330
+ }
331
+ export type RpdDataModelIndexPropertyConfig = string | {
332
+ code: string;
333
+ order?: "asc" | "desc";
334
+ };
335
+ export type RpdDataModelIndexOptions = FindEntityRelationalFilterOptions | FindEntitySetFilterOptions | FindEntityLogicalFilterOptions<RpdDataModelIndexOptions> | FindEntityUnaryFilterOptions;
323
336
  export type EventHandler<T = any> = (sender: RapidPlugin, payload: T) => void;
324
337
  export interface RpdRoute {
325
338
  name: string;
@@ -381,9 +394,9 @@ export interface FindEntitySetFilterOptions {
381
394
  value: any[];
382
395
  itemType?: string;
383
396
  }
384
- export interface FindEntityLogicalFilterOptions {
397
+ export interface FindEntityLogicalFilterOptions<TFilter = EntityFilterOptions> {
385
398
  operator: EntityFilterLogicalOperators;
386
- filters: EntityFilterOptions[];
399
+ filters: TFilter[];
387
400
  }
388
401
  export interface FindEntityUnaryFilterOptions {
389
402
  field: string;
@@ -421,6 +434,7 @@ export interface DeleteEntityByIdOptions {
421
434
  export interface CreateEntityOptions {
422
435
  routeContext?: RouteContext;
423
436
  entity: any;
437
+ postponeUniquenessCheck?: boolean;
424
438
  }
425
439
  export interface UpdateEntityOptions {
426
440
  routeContext?: RouteContext;
@@ -435,6 +449,7 @@ export interface UpdateEntityByIdOptions {
435
449
  type: string;
436
450
  };
437
451
  stateProperties?: string[];
452
+ postponeUniquenessCheck?: boolean;
438
453
  }
439
454
  export interface DeleteEntityOptions {
440
455
  routeContext?: RouteContext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",