@ruiapp/rapid-core 0.2.4 → 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.
- package/dist/helpers/filterHelper.d.ts +3 -2
- package/dist/index.js +43 -10
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
- package/src/bootstrapApplicationConfig.ts +8 -0
- package/src/dataAccess/entityManager.ts +17 -0
- package/src/helpers/entityHelpers.ts +1 -1
- package/src/helpers/filterHelper.ts +21 -8
- package/src/plugins/metaManage/MetaManagePlugin.ts +2 -1
- package/src/types.ts +5 -0
|
@@ -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",
|
|
@@ -2217,7 +2225,7 @@ function getEntityPartChanges(server, model, before, after) {
|
|
|
2217
2225
|
let changes = {};
|
|
2218
2226
|
for (const key in after) {
|
|
2219
2227
|
const property = getEntityPropertyByCode(server, model, key);
|
|
2220
|
-
if (isOneRelationProperty(property)) {
|
|
2228
|
+
if (property && isOneRelationProperty(property)) {
|
|
2221
2229
|
const afterValue = after[key];
|
|
2222
2230
|
const beforeValue = before[key] || before[property.targetIdColumnName];
|
|
2223
2231
|
if (afterValue) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
@@ -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) {
|
|
@@ -16,7 +16,7 @@ export function getEntityPartChanges(server: IRpdServer, model: RpdDataModel, be
|
|
|
16
16
|
let changes = {};
|
|
17
17
|
for (const key in after) {
|
|
18
18
|
const property = getEntityPropertyByCode(server, model, key);
|
|
19
|
-
if (isOneRelationProperty(property)) {
|
|
19
|
+
if (property && isOneRelationProperty(property)) {
|
|
20
20
|
const afterValue: number | { id: number } | null = after[key];
|
|
21
21
|
const beforeValue: number | { id: number } | null = before[key] || before[property.targetIdColumnName];
|
|
22
22
|
if (afterValue) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|