@ruiapp/rapid-core 0.2.5 → 0.2.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.
@@ -1,5 +1,6 @@
1
1
  import { EntityFilterOptions, RpdDataModel, RpdDataModelIndexOptions } from "../types";
2
2
  import { RowFilterOptions } from "../dataAccess/dataAccessTypes";
3
+ import { Logger } from "../facilities/log/LogFacility";
3
4
  export declare function removeFiltersWithNullValue(filters?: EntityFilterOptions[]): EntityFilterOptions[];
4
- export declare function convertModelIndexConditionsToRowFilterOptions(model: RpdDataModel, filters: RpdDataModelIndexOptions[]): RowFilterOptions[];
5
- export declare function replaceModelIndexConditionEntityPropertyWithTableColumn(model: RpdDataModel, filter: RpdDataModelIndexOptions): RowFilterOptions;
5
+ export declare function convertModelIndexConditionsToRowFilterOptions(logger: Logger, model: RpdDataModel, filters: RpdDataModelIndexOptions[]): RowFilterOptions[];
6
+ export declare function replaceModelIndexConditionEntityPropertyWithTableColumn(logger: Logger, model: RpdDataModel, filter: RpdDataModelIndexOptions): RowFilterOptions;
package/dist/index.js CHANGED
@@ -1649,6 +1649,14 @@ var bootstrapApplicationConfig = {
1649
1649
  type: "text",
1650
1650
  required: false,
1651
1651
  },
1652
+ {
1653
+ name: "readonly",
1654
+ code: "readonly",
1655
+ columnName: "readonly",
1656
+ type: "boolean",
1657
+ required: true,
1658
+ defaultValue: "false",
1659
+ },
1652
1660
  {
1653
1661
  name: "config",
1654
1662
  code: "config",
@@ -3157,6 +3165,23 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
3157
3165
  routeContext: options.routeContext,
3158
3166
  });
3159
3167
  changes = getEntityPartChanges(server, model, entity, entityToSave);
3168
+ // check readonly properties
3169
+ Object.keys(changes).forEach((propertyName) => {
3170
+ let isReadonlyProperty = false;
3171
+ const property = getEntityPropertyByCode(server, model, propertyName);
3172
+ if (property && property.readonly) {
3173
+ isReadonlyProperty = true;
3174
+ }
3175
+ else {
3176
+ const oneRelationProperty = getEntityProperty(server, model, (item) => item.relation === "one" && item.targetIdColumnName === propertyName);
3177
+ if (oneRelationProperty && oneRelationProperty.readonly) {
3178
+ isReadonlyProperty = true;
3179
+ }
3180
+ }
3181
+ if (isReadonlyProperty) {
3182
+ throw new Error(`Updating "${property.name}" property is not allowed because it's readonly.`);
3183
+ }
3184
+ });
3160
3185
  // check unique constraints
3161
3186
  if (!options.postponeUniquenessCheck) {
3162
3187
  if (model.indexes && model.indexes.length) {
@@ -4215,7 +4240,7 @@ function transformFilterWithSubFilters(filter) {
4215
4240
  filter.filters = subFilters;
4216
4241
  return filter;
4217
4242
  }
4218
- function convertModelIndexConditionsToRowFilterOptions(model, filters) {
4243
+ function convertModelIndexConditionsToRowFilterOptions(logger, model, filters) {
4219
4244
  if (!filters || !filters.length) {
4220
4245
  return [];
4221
4246
  }
@@ -4225,16 +4250,19 @@ function convertModelIndexConditionsToRowFilterOptions(model, filters) {
4225
4250
  if (operator === "and" || operator === "or") {
4226
4251
  replacedFilters.push({
4227
4252
  operator: operator,
4228
- filters: convertModelIndexConditionsToRowFilterOptions(model, filter.filters),
4253
+ filters: convertModelIndexConditionsToRowFilterOptions(logger, model, filter.filters),
4229
4254
  });
4230
4255
  }
4231
4256
  else {
4232
- replacedFilters.push(replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter));
4257
+ const replacedFilter = replaceModelIndexConditionEntityPropertyWithTableColumn(logger, model, filter);
4258
+ if (replacedFilter) {
4259
+ replacedFilters.push(replacedFilter);
4260
+ }
4233
4261
  }
4234
4262
  }
4235
4263
  return replacedFilters;
4236
4264
  }
4237
- function replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter) {
4265
+ function replaceModelIndexConditionEntityPropertyWithTableColumn(logger, model, filter) {
4238
4266
  const { operator } = filter;
4239
4267
  const filterField = filter.field;
4240
4268
  let property = getEntityOwnPropertyByCode(model, filterField);
@@ -4248,14 +4276,16 @@ function replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter)
4248
4276
  }
4249
4277
  }
4250
4278
  else if (isManyRelationProperty(property)) {
4251
- throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
4279
+ logger.warn(`Index condition on many-relation property "${property.code}" is not supported.`);
4280
+ return null;
4252
4281
  }
4253
4282
  else {
4254
4283
  columnName = property.columnName || property.code;
4255
4284
  }
4256
4285
  }
4257
4286
  else if (operator === "exists" || operator === "notExists") {
4258
- throw new Error(`"exists" and "notExists" operators are not supported in index conditions.`);
4287
+ logger.warn(`"exists" and "notExists" operators are not supported in index conditions.`);
4288
+ return null;
4259
4289
  }
4260
4290
  else {
4261
4291
  property = getEntityOwnProperty(model, (property) => {
@@ -4271,7 +4301,8 @@ function replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter)
4271
4301
  });
4272
4302
  if (property) {
4273
4303
  if (isManyRelationProperty(property)) {
4274
- throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
4304
+ logger.warn(`Index condition on many-relation property "${property.code}" is not supported.`);
4305
+ return null;
4275
4306
  }
4276
4307
  columnName = property.targetIdColumnName;
4277
4308
  if (lodash.isPlainObject(filterValue)) {
@@ -4279,7 +4310,8 @@ function replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter)
4279
4310
  }
4280
4311
  }
4281
4312
  else {
4282
- throw new Error(`Unknown field "${filterField}" in index conditions.`);
4313
+ logger.warn(`Unknown field "${filterField}" in index conditions.`);
4314
+ return null;
4283
4315
  }
4284
4316
  }
4285
4317
  }
@@ -4651,7 +4683,8 @@ function generateTableIndexDDL(queryBuilder, server, model, index) {
4651
4683
  tableName: model.tableName,
4652
4684
  })} (${indexColumns.join(", ")})`;
4653
4685
  if (index.conditions) {
4654
- const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(model, index.conditions);
4686
+ const logger = server.getLogger();
4687
+ const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(logger, model, index.conditions);
4655
4688
  ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, rowFilterOptions)}`;
4656
4689
  }
4657
4690
  return ddl;
package/dist/types.d.ts CHANGED
@@ -272,6 +272,10 @@ export interface RpdDataModelProperty {
272
272
  * 当设置了 linkTableName 时,可以设置关联关系表所在的 Schema。
273
273
  */
274
274
  linkSchema?: string;
275
+ /**
276
+ * 当设置为`true`时,仅允许在创建时设置此属性的值,不允许更新。
277
+ */
278
+ readonly?: boolean;
275
279
  }
276
280
  export type RpdDataPropertyTypes = "integer" | "long" | "float" | "double" | "decimal" | "text" | "boolean" | "date" | "time" | "datetime" | "json" | "relation" | "relation[]" | "option" | "option[]" | "file" | "file[]" | "image" | "image[]";
277
281
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -229,6 +229,14 @@ export default {
229
229
  type: "text",
230
230
  required: false,
231
231
  },
232
+ {
233
+ name: "readonly",
234
+ code: "readonly",
235
+ columnName: "readonly",
236
+ type: "boolean",
237
+ required: true,
238
+ defaultValue: "false",
239
+ },
232
240
  {
233
241
  name: "config",
234
242
  code: "config",
@@ -1008,6 +1008,23 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
1008
1008
 
1009
1009
  changes = getEntityPartChanges(server, model, entity, entityToSave);
1010
1010
 
1011
+ // check readonly properties
1012
+ Object.keys(changes).forEach((propertyName) => {
1013
+ let isReadonlyProperty = false;
1014
+ const property = getEntityPropertyByCode(server, model, propertyName);
1015
+ if (property && property.readonly) {
1016
+ isReadonlyProperty = true;
1017
+ } else {
1018
+ const oneRelationProperty = getEntityProperty(server, model, (item) => item.relation === "one" && item.targetIdColumnName === propertyName);
1019
+ if (oneRelationProperty && oneRelationProperty.readonly) {
1020
+ isReadonlyProperty = true;
1021
+ }
1022
+ }
1023
+ if (isReadonlyProperty) {
1024
+ throw new Error(`Updating "${property.name}" property is not allowed because it's readonly.`);
1025
+ }
1026
+ });
1027
+
1011
1028
  // check unique constraints
1012
1029
  if (!options.postponeUniquenessCheck) {
1013
1030
  if (model.indexes && model.indexes.length) {
@@ -12,6 +12,7 @@ import { getEntityOwnProperty, getEntityOwnPropertyByCode, isManyRelationPropert
12
12
  import { IRpdServer } from "~/core/server";
13
13
  import { isPlainObject } from "lodash";
14
14
  import { RowFilterOptions } from "~/dataAccess/dataAccessTypes";
15
+ import { Logger } from "~/facilities/log/LogFacility";
15
16
 
16
17
  export function removeFiltersWithNullValue(filters?: EntityFilterOptions[]) {
17
18
  const result: EntityFilterOptions[] = [];
@@ -58,7 +59,7 @@ function transformFilterWithSubFilters(
58
59
  return filter;
59
60
  }
60
61
 
61
- export function convertModelIndexConditionsToRowFilterOptions(model: RpdDataModel, filters: RpdDataModelIndexOptions[]) {
62
+ export function convertModelIndexConditionsToRowFilterOptions(logger: Logger, model: RpdDataModel, filters: RpdDataModelIndexOptions[]) {
62
63
  if (!filters || !filters.length) {
63
64
  return [];
64
65
  }
@@ -69,15 +70,23 @@ export function convertModelIndexConditionsToRowFilterOptions(model: RpdDataMode
69
70
  if (operator === "and" || operator === "or") {
70
71
  replacedFilters.push({
71
72
  operator: operator,
72
- filters: convertModelIndexConditionsToRowFilterOptions(model, filter.filters),
73
+ filters: convertModelIndexConditionsToRowFilterOptions(logger, model, filter.filters),
73
74
  });
74
75
  } else {
75
- replacedFilters.push(replaceModelIndexConditionEntityPropertyWithTableColumn(model, filter));
76
+ const replacedFilter = replaceModelIndexConditionEntityPropertyWithTableColumn(logger, model, filter);
77
+ if (replacedFilter) {
78
+ replacedFilters.push(replacedFilter);
79
+ }
76
80
  }
77
81
  }
78
82
  return replacedFilters;
79
83
  }
80
- export function replaceModelIndexConditionEntityPropertyWithTableColumn(model: RpdDataModel, filter: RpdDataModelIndexOptions): RowFilterOptions {
84
+
85
+ export function replaceModelIndexConditionEntityPropertyWithTableColumn(
86
+ logger: Logger,
87
+ model: RpdDataModel,
88
+ filter: RpdDataModelIndexOptions,
89
+ ): RowFilterOptions {
81
90
  const { operator } = filter;
82
91
  const filterField = (filter as EntityNonRelationPropertyFilterOptions).field;
83
92
  let property: RpdDataModelProperty = getEntityOwnPropertyByCode(model, filterField);
@@ -92,12 +101,14 @@ export function replaceModelIndexConditionEntityPropertyWithTableColumn(model: R
92
101
  filterValue = filterValue.id;
93
102
  }
94
103
  } else if (isManyRelationProperty(property)) {
95
- throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
104
+ logger.warn(`Index condition on many-relation property "${property.code}" is not supported.`);
105
+ return null;
96
106
  } else {
97
107
  columnName = property.columnName || property.code;
98
108
  }
99
109
  } else if ((operator as any) === "exists" || (operator as any) === "notExists") {
100
- throw new Error(`"exists" and "notExists" operators are not supported in index conditions.`);
110
+ logger.warn(`"exists" and "notExists" operators are not supported in index conditions.`);
111
+ return null;
101
112
  } else {
102
113
  property = getEntityOwnProperty(model, (property) => {
103
114
  return property.columnName === filterField;
@@ -113,14 +124,16 @@ export function replaceModelIndexConditionEntityPropertyWithTableColumn(model: R
113
124
 
114
125
  if (property) {
115
126
  if (isManyRelationProperty(property)) {
116
- throw new Error(`Condition on many-relation property "${property.code}" is not supported.`);
127
+ logger.warn(`Index condition on many-relation property "${property.code}" is not supported.`);
128
+ return null;
117
129
  }
118
130
  columnName = property.targetIdColumnName;
119
131
  if (isPlainObject(filterValue)) {
120
132
  filterValue = filterValue.id;
121
133
  }
122
134
  } else {
123
- throw new Error(`Unknown field "${filterField}" in index conditions.`);
135
+ logger.warn(`Unknown field "${filterField}" in index conditions.`);
136
+ return null;
124
137
  }
125
138
  }
126
139
  }
@@ -495,7 +495,8 @@ function generateTableIndexDDL(queryBuilder: IQueryBuilder, server: IRpdServer,
495
495
  })} (${indexColumns.join(", ")})`;
496
496
 
497
497
  if (index.conditions) {
498
- const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(model, index.conditions);
498
+ const logger = server.getLogger();
499
+ const rowFilterOptions = convertModelIndexConditionsToRowFilterOptions(logger, model, index.conditions);
499
500
  ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, rowFilterOptions)}`;
500
501
  }
501
502
 
package/src/types.ts CHANGED
@@ -302,6 +302,11 @@ export interface RpdDataModelProperty {
302
302
  * 当设置了 linkTableName 时,可以设置关联关系表所在的 Schema。
303
303
  */
304
304
  linkSchema?: string;
305
+
306
+ /**
307
+ * 当设置为`true`时,仅允许在创建时设置此属性的值,不允许更新。
308
+ */
309
+ readonly?: boolean;
305
310
  }
306
311
 
307
312
  export type RpdDataPropertyTypes =